per DB's patch, overhaul the rcfile and history file reading and writing

routines to fix a few fundamental problems and limitations; also add
getline() and getdelim() equivalents adapted from GNU mailutils 0.5 (and
tweaked to better integrate with nano), since the patch uses getline()


git-svn-id: svn://svn.savannah.gnu.org/nano/trunk/nano@1900 35c25a1d-7b9e-4130-9fde-d3aeb78583b8
This commit is contained in:
David Lawrence Ramsey 2004-08-17 05:23:38 +00:00
parent bb50b305d5
commit a27bd65057
9 changed files with 194 additions and 100 deletions

View File

@ -92,6 +92,14 @@ CVS code -
fill_flag_used to avoid warnings when compiling with
--disable-wrapping, --disable-justify, or a combination of the
two. (DLR)
- Overhaul the routines used to read the rcfiles and history
files for efficiency, make them work properly on lines over
1023 characters long and on lines containing nulls, and make
them properly handle the case where the user's home directory
changes in the middle of a session. New functions
histfilename() and writehist(); changes to
thanks_for_all_the_fish(), load_history(), save_history(), and
do_rcfile(). (David Benbennick)
- files.c:
get_next_filename()
- Tweak for efficiency, and add the ".save" suffix to the file
@ -112,7 +120,7 @@ CVS code -
- global.c:
shortcut_init()
- Fix erroneous #ifdef so that nano compiles with
--disable-justify again. (DLR; found by Mike Frysinger)
--disable-justify again. (DLR, found by Mike Frysinger)
- Change the Cancel shortcut in the file browser to an Exit
shortcut, to be more compatible with the current version of
Pico. (DLR)
@ -186,8 +194,8 @@ CVS code -
match their corresponding location in files.c. (DLR)
- rcfile.c:
rcfile_msg()
- Removed along with the related static int errors, and replaced
with calls to rcfile_error(). (David Benbennick)
- Removed and replaced with calls to rcfile_error(). (David
Benbennick)
- Removed the reference to "starting nano" in the statusbar
message, as it may be called when we exit if the history file
can't be saved. (DLR)
@ -225,9 +233,13 @@ CVS code -
- Remove code chacking for n's being less than 0 that will never
be run, since n is a size_t and is hence unsigned. (David
Benbennick)
ngetdelim(), ngetline()
- New functions equivalent to getdelim() and getline(), which
are both GNU extensions. (DLR, adapted from GNU mailutils
0.5)
- winio.c:
get_kbinput()
- Since the only valid values for escapes are 0, 1, and 2,
- Since the only valid values for escapes are 0, 1, and 2,
convert it to an int. (DLR)
get_control_kbinput()
- Fix erroneous debugging statement so that nano compiles with
@ -254,6 +266,8 @@ CVS code -
- configure.ac:
- Add AC_PROG_LN_S, so that we can portably create symlinks.
(DLR)
- Check for getdelim() and getline(), which are both GNU
extensions. (DLR)
- nanorc.sample:
- Add sample regexes for patch files. (Mike Frysinger)
- Various improvements to the "c-file" regexes. Add double,

View File

@ -290,7 +290,7 @@ AC_MSG_WARN([*** Can not use slang when cross-compiling])),
esac], [AC_MSG_RESULT(no)])
dnl Checks for functions
AC_CHECK_FUNCS(snprintf vsnprintf isblank strcasecmp strncasecmp strcasestr strnlen)
AC_CHECK_FUNCS(snprintf vsnprintf isblank strcasecmp strncasecmp strcasestr strnlen getline getdelim)
if test "x$ac_cv_func_snprintf" = "xno" -o "xac_cv_func_vsnprintf" = "xno"
then
AM_PATH_GLIB_2_0(2.0.0,,

View File

@ -2914,30 +2914,30 @@ char *do_browse_from(const char *inpath)
#endif /* !DISABLE_BROWSER */
#if !defined(NANO_SMALL) && defined(ENABLE_NANORC)
/* Return $HOME/.nano_history, or NULL if we can't find the homedir.
* The string is dynamically allocated, and should be freed. */
char *histfilename(void)
{
char *nanohist = NULL;
if (homedir != NULL) {
size_t homelen = strlen(homedir);
nanohist = charalloc(homelen + 15);
strcpy(nanohist, homedir);
strcpy(nanohist + homelen, "/.nano_history");
}
return nanohist;
}
void load_history(void)
{
FILE *hist;
const struct passwd *userage = NULL;
static char *nanohist;
char *buf, *ptr;
char *homenv = getenv("HOME");
historyheadtype *history = &search_history;
if (homenv != NULL) {
nanohist = charealloc(nanohist, strlen(homenv) + 15);
sprintf(nanohist, "%s/.nano_history", homenv);
} else {
userage = getpwuid(geteuid());
endpwent();
nanohist = charealloc(nanohist, strlen(userage->pw_dir) + 15);
sprintf(nanohist, "%s/.nano_history", userage->pw_dir);
}
char *nanohist = histfilename();
/* assume do_rcfile() has reported missing home dir */
if (nanohist != NULL) {
FILE *hist = fopen(nanohist, "r");
if (homenv != NULL || userage != NULL) {
hist = fopen(nanohist, "r");
if (hist == NULL) {
if (errno != ENOENT) {
/* Don't save history when we quit. */
@ -2947,80 +2947,72 @@ void load_history(void)
while (getchar() != '\n')
;
}
free(nanohist);
} else {
buf = charalloc(1024);
while (fgets(buf, 1023, hist) != 0) {
ptr = buf;
while (*ptr != '\n' && *ptr != '\0' && ptr < buf + 1023)
ptr++;
*ptr = '\0';
if (strlen(buf))
update_history(history, buf);
else
historyheadtype *history = &search_history;
char *line = NULL;
size_t buflen = 0;
ssize_t read;
while ((read = getline(&line, &buflen, hist)) >= 0) {
if (read > 0 && line[read - 1] == '\n') {
read--;
line[read] = '\0';
}
if (read > 0) {
unsunder(line, read);
update_history(history, line);
} else
history = &replace_history;
}
fclose(hist);
free(buf);
free(nanohist);
free(line);
UNSET(HISTORY_CHANGED);
}
free(nanohist);
}
}
bool writehist(FILE *hist, historyheadtype *histhead)
{
historytype *h;
/* write oldest first */
for (h = histhead->tail; h->prev != NULL; h = h->prev) {
size_t len = strlen(h->data);
sunder(h->data);
if (fwrite(h->data, sizeof(char), len, hist) < len ||
putc('\n', hist) == EOF)
return FALSE;
}
return TRUE;
}
/* save histories to ~/.nano_history */
void save_history(void)
{
FILE *hist;
const struct passwd *userage = NULL;
char *nanohist = NULL;
char *homenv = getenv("HOME");
historytype *h;
char *nanohist;
/* don't save unchanged or empty histories */
if ((search_history.count == 0 && replace_history.count == 0) ||
!ISSET(HISTORY_CHANGED) || ISSET(VIEW_MODE))
return;
if (homenv != NULL) {
nanohist = charealloc(nanohist, strlen(homenv) + 15);
sprintf(nanohist, "%s/.nano_history", homenv);
} else {
userage = getpwuid(geteuid());
endpwent();
nanohist = charealloc(nanohist, strlen(userage->pw_dir) + 15);
sprintf(nanohist, "%s/.nano_history", userage->pw_dir);
}
nanohist = histfilename();
if (nanohist != NULL) {
FILE *hist = fopen(nanohist, "wb");
if (homenv != NULL || userage != NULL) {
hist = fopen(nanohist, "wb");
if (hist == NULL)
rcfile_error(N_("Error writing %s: %s"), nanohist, strerror(errno));
else {
/* set rw only by owner for security ?? */
chmod(nanohist, S_IRUSR | S_IWUSR);
/* write oldest first */
for (h = search_history.tail; h->prev; h = h->prev) {
h->data = charealloc(h->data, strlen(h->data) + 2);
strcat(h->data, "\n");
if (fputs(h->data, hist) == EOF) {
rcfile_error(N_("Error writing %s: %s"), nanohist, strerror(errno));
goto come_from;
}
}
if (fputs("\n", hist) == EOF) {
if (!writehist(hist, &search_history) ||
putc('\n', hist) == EOF ||
!writehist(hist, &replace_history))
rcfile_error(N_("Error writing %s: %s"), nanohist, strerror(errno));
goto come_from;
}
for (h = replace_history.tail; h->prev; h = h->prev) {
h->data = charealloc(h->data, strlen(h->data) + 2);
strcat(h->data, "\n");
if (fputs(h->data, hist) == EOF) {
rcfile_error(N_("Error writing %s: %s"), nanohist, strerror(errno));
goto come_from;
}
}
come_from:
fclose(hist);
}
free(nanohist);

View File

@ -178,6 +178,10 @@ bool curses_ended = FALSE; /* Indicates to statusbar() to simply
* write to stderr, since endwin() has
* ended curses mode. */
#ifdef ENABLE_NANORC
char *homedir = NULL; /* $HOME or from /etc/passwd. */
#endif
size_t length_of_list(const shortcut *s)
{
size_t i = 0;
@ -1188,5 +1192,8 @@ void thanks_for_all_the_fish(void)
free_history(&search_history);
free_history(&replace_history);
#endif
#ifdef ENABLE_NANORC
free(homedir);
#endif
}
#endif /* DEBUG */

View File

@ -3000,7 +3000,7 @@ void terminal_init(void)
disable_flow_control();
}
int main(int argc, char *argv[])
int main(int argc, char **argv)
{
int optchr;
int startline = 0; /* Line to try and start at */

View File

@ -95,8 +95,8 @@
# endif
#endif
/* If no isblank(), strcasecmp(), strncasecmp(), strcasestr(), or
* strnlen(), use the versions we have. */
/* If no isblank(), strcasecmp(), strncasecmp(), strcasestr(),
* strnlen(), getdelim(), or getline(), use the versions we have. */
#ifndef HAVE_ISBLANK
#define isblank is_blank_char
#endif
@ -117,6 +117,14 @@
#define strnlen nstrnlen
#endif
#ifndef HAVE_GETDELIM
#define getdelim ngetdelim
#endif
#ifndef HAVE_GETLINE
#define getline ngetline
#endif
/* Assume ERR is defined as -1. To avoid duplicate case values when
* some key definitions are missing, we have to set all of these, and
* all of the special sentinel values below, to different negative

View File

@ -142,6 +142,10 @@ extern historyheadtype replace_history;
extern bool curses_ended;
#ifdef ENABLE_NANORC
extern char *homedir;
#endif
/* Functions we want available. */
/* Public functions in color.c */
@ -224,7 +228,9 @@ char *do_browser(const char *inpath);
char *do_browse_from(const char *inpath);
#endif
#if !defined(NANO_SMALL) && defined(ENABLE_NANORC)
char *histfilename(void);
void load_history(void);
bool writehist(FILE *hist, historyheadtype *histhead);
void save_history(void);
#endif
@ -461,6 +467,12 @@ const char *revstristr(const char *haystack, const char *needle, const
#ifndef HAVE_STRNLEN
size_t nstrnlen(const char *s, size_t maxlen);
#endif
#ifndef HAVE_GETLINE
ssize_t ngetline(char **lineptr, size_t *n, FILE *stream);
#endif
#ifndef HAVE_GETDELIM
ssize_t ngetdelim(char **lineptr, size_t *n, int delim, FILE *stream);
#endif
const char *strstrwrapper(const char *haystack, const char *needle,
const char *start);
void nperror(const char *s);

View File

@ -101,7 +101,7 @@ const static rcoption rcopts[] = {
static bool errors = FALSE;
static int lineno = 0;
static char *nanorc;
static const char *nanorc;
/* We have an error in some part of the rcfile; put it on stderr and
make the user hit return to continue starting up nano. */
@ -648,16 +648,13 @@ void parse_rcfile(FILE *rcstream)
void do_rcfile(void)
{
FILE *rcstream;
const struct passwd *userage;
uid_t euid = geteuid();
char *homenv = getenv("HOME");
#ifdef SYSCONFDIR
assert(sizeof(SYSCONFDIR) == strlen(SYSCONFDIR) + 1);
nanorc = charalloc(sizeof(SYSCONFDIR) + 7);
sprintf(nanorc, "%s/nanorc", SYSCONFDIR);
nanorc = SYSCONFDIR "/nanorc";
/* Try to open system nanorc */
if ((rcstream = fopen(nanorc, "r")) != NULL) {
rcstream = fopen(nanorc, "r");
if (rcstream != NULL) {
/* Parse it! */
parse_rcfile(rcstream);
fclose(rcstream);
@ -666,33 +663,38 @@ void do_rcfile(void)
lineno = 0;
/* Rely on $HOME, fall back on getpwuid() */
if (homenv != NULL) {
nanorc = charealloc(nanorc, strlen(homenv) + 10);
sprintf(nanorc, "%s/.nanorc", homenv);
} else {
userage = getpwuid(euid);
endpwent();
{
const char *homenv = getenv("HOME");
if (userage == NULL) {
rcfile_error(N_("I can't find my home directory! Wah!"));
SET(NO_RCFILE);
} else {
nanorc = charealloc(nanorc, strlen(userage->pw_dir) + 9);
sprintf(nanorc, "%s/.nanorc", userage->pw_dir);
/* Rely on $HOME, fall back on getpwuid() */
if (homenv == NULL) {
const struct passwd *userage = getpwuid(geteuid());
if (userage != NULL)
homenv = userage->pw_dir;
}
homedir = mallocstrcpy(NULL, homenv);
}
if (!ISSET(NO_RCFILE)) {
if (homedir == NULL) {
rcfile_error(N_("I can't find my home directory! Wah!"));
SET(NO_RCFILE);
} else {
size_t homelen = strlen(homedir);
char *nanorcf = charalloc(homelen + 9);
nanorc = nanorcf;
strcpy(nanorcf, homedir);
strcpy(nanorcf + homelen, "/.nanorc");
#if defined(DISABLE_ROOTWRAP) && !defined(DISABLE_WRAPPING)
/* If we've already read SYSCONFDIR/nanorc (if it's there), we're
root, and --disable-wrapping-as-root is used, turn wrapping off */
if (euid == NANO_ROOT_UID)
if (geteuid() == NANO_ROOT_UID)
SET(NO_WRAP);
#endif
if ((rcstream = fopen(nanorc, "r")) == NULL) {
rcstream = fopen(nanorc, "r");
if (rcstream == NULL) {
/* Don't complain about the file not existing */
if (errno != ENOENT) {
rcfile_error(N_("Error reading %s: %s"), nanorc, strerror(errno));
@ -702,10 +704,10 @@ void do_rcfile(void)
parse_rcfile(rcstream);
fclose(rcstream);
}
free(nanorcf);
}
lineno = 0;
free(nanorc);
#ifdef ENABLE_COLOR
set_colorpairs();
#endif

View File

@ -240,6 +240,65 @@ size_t nstrnlen(const char *s, size_t maxlen)
}
#endif
#ifndef HAVE_GETLINE
/* This function is equivalent to getline(). It was adapted from
* GNU mailutils' getline() function. */
ssize_t ngetline(char **lineptr, size_t *n, FILE *stream)
{
return getdelim(lineptr, n, '\n', stream);
}
#endif
#ifndef HAVE_GETDELIM
/* This function is equivalent to getdelim(). It was adapted from
* GNU mailutils' getdelim() function. */
ssize_t ngetdelim(char **lineptr, size_t *n, int delim, FILE *stream)
{
static const int line_size = 128;
/* Default value for line length. */
size_t indx = 0;
int c;
/* Sanity checks. */
if (lineptr == NULL || n == NULL || stream == NULL)
return -1;
/* Allocate the line the first time. */
if (*lineptr == NULL) {
*lineptr = charalloc(line_size);
*n = line_size;
}
while ((c = getc(stream)) != EOF) {
/* Check if more memory is needed. */
if (indx >= *n) {
*lineptr = charealloc(*lineptr, *n + line_size);
*n += line_size;
}
/* Push the result in the line. */
(*lineptr)[indx++] = (char)c;
/* Bail out. */
if (c == delim)
break;
}
/* Make room for the null character. */
if (indx >= *n) {
*lineptr = charealloc(*lineptr, *n + line_size);
*n += line_size;
}
/* Null terminate the buffer. */
(*lineptr)[indx++] = '\0';
/* The last line may not have the delimiter, we have to return what
* we got and the error will be seen on the next iteration. */
return (c == EOF && (indx - 1) == 0) ? -1 : indx - 1;
}
#endif
/* If we are searching backwards, we will find the last match that
* starts no later than start. Otherwise we find the first match
* starting no earlier than start. If we are doing a regexp search, we