Commit a27bd650 authored by David Lawrence Ramsey's avatar David Lawrence Ramsey
Browse files

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
No related merge requests found
Showing with 192 additions and 98 deletions
+192 -98
...@@ -92,6 +92,14 @@ CVS code - ...@@ -92,6 +92,14 @@ CVS code -
fill_flag_used to avoid warnings when compiling with fill_flag_used to avoid warnings when compiling with
--disable-wrapping, --disable-justify, or a combination of the --disable-wrapping, --disable-justify, or a combination of the
two. (DLR) 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: - files.c:
get_next_filename() get_next_filename()
- Tweak for efficiency, and add the ".save" suffix to the file - Tweak for efficiency, and add the ".save" suffix to the file
...@@ -112,7 +120,7 @@ CVS code - ...@@ -112,7 +120,7 @@ CVS code -
- global.c: - global.c:
shortcut_init() shortcut_init()
- Fix erroneous #ifdef so that nano compiles with - 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 - Change the Cancel shortcut in the file browser to an Exit
shortcut, to be more compatible with the current version of shortcut, to be more compatible with the current version of
Pico. (DLR) Pico. (DLR)
...@@ -186,8 +194,8 @@ CVS code - ...@@ -186,8 +194,8 @@ CVS code -
match their corresponding location in files.c. (DLR) match their corresponding location in files.c. (DLR)
- rcfile.c: - rcfile.c:
rcfile_msg() rcfile_msg()
- Removed along with the related static int errors, and replaced - Removed and replaced with calls to rcfile_error(). (David
with calls to rcfile_error(). (David Benbennick) Benbennick)
- Removed the reference to "starting nano" in the statusbar - Removed the reference to "starting nano" in the statusbar
message, as it may be called when we exit if the history file message, as it may be called when we exit if the history file
can't be saved. (DLR) can't be saved. (DLR)
...@@ -225,6 +233,10 @@ CVS code - ...@@ -225,6 +233,10 @@ CVS code -
- Remove code chacking for n's being less than 0 that will never - 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 be run, since n is a size_t and is hence unsigned. (David
Benbennick) Benbennick)
ngetdelim(), ngetline()
- New functions equivalent to getdelim() and getline(), which
are both GNU extensions. (DLR, adapted from GNU mailutils
0.5)
- winio.c: - winio.c:
get_kbinput() 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,
...@@ -254,6 +266,8 @@ CVS code - ...@@ -254,6 +266,8 @@ CVS code -
- configure.ac: - configure.ac:
- Add AC_PROG_LN_S, so that we can portably create symlinks. - Add AC_PROG_LN_S, so that we can portably create symlinks.
(DLR) (DLR)
- Check for getdelim() and getline(), which are both GNU
extensions. (DLR)
- nanorc.sample: - nanorc.sample:
- Add sample regexes for patch files. (Mike Frysinger) - Add sample regexes for patch files. (Mike Frysinger)
- Various improvements to the "c-file" regexes. Add double, - Various improvements to the "c-file" regexes. Add double,
......
...@@ -290,7 +290,7 @@ AC_MSG_WARN([*** Can not use slang when cross-compiling])), ...@@ -290,7 +290,7 @@ AC_MSG_WARN([*** Can not use slang when cross-compiling])),
esac], [AC_MSG_RESULT(no)]) esac], [AC_MSG_RESULT(no)])
dnl Checks for functions 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" if test "x$ac_cv_func_snprintf" = "xno" -o "xac_cv_func_vsnprintf" = "xno"
then then
AM_PATH_GLIB_2_0(2.0.0,, AM_PATH_GLIB_2_0(2.0.0,,
......
...@@ -2914,30 +2914,30 @@ char *do_browse_from(const char *inpath) ...@@ -2914,30 +2914,30 @@ char *do_browse_from(const char *inpath)
#endif /* !DISABLE_BROWSER */ #endif /* !DISABLE_BROWSER */
#if !defined(NANO_SMALL) && defined(ENABLE_NANORC) #if !defined(NANO_SMALL) && defined(ENABLE_NANORC)
void load_history(void) /* 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)
{ {
FILE *hist; char *nanohist = NULL;
const struct passwd *userage = NULL;
static char *nanohist;
char *buf, *ptr;
char *homenv = getenv("HOME");
historyheadtype *history = &search_history;
if (homedir != NULL) {
size_t homelen = strlen(homedir);
if (homenv != NULL) { nanohist = charalloc(homelen + 15);
nanohist = charealloc(nanohist, strlen(homenv) + 15); strcpy(nanohist, homedir);
sprintf(nanohist, "%s/.nano_history", homenv); strcpy(nanohist + homelen, "/.nano_history");
} else {
userage = getpwuid(geteuid());
endpwent();
nanohist = charealloc(nanohist, strlen(userage->pw_dir) + 15);
sprintf(nanohist, "%s/.nano_history", userage->pw_dir);
} }
return nanohist;
}
void load_history(void)
{
char *nanohist = histfilename();
/* assume do_rcfile() has reported missing home dir */ /* 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 (hist == NULL) {
if (errno != ENOENT) { if (errno != ENOENT) {
/* Don't save history when we quit. */ /* Don't save history when we quit. */
...@@ -2947,80 +2947,72 @@ void load_history(void) ...@@ -2947,80 +2947,72 @@ void load_history(void)
while (getchar() != '\n') while (getchar() != '\n')
; ;
} }
free(nanohist);
} else { } else {
buf = charalloc(1024); historyheadtype *history = &search_history;
while (fgets(buf, 1023, hist) != 0) { char *line = NULL;
ptr = buf; size_t buflen = 0;
while (*ptr != '\n' && *ptr != '\0' && ptr < buf + 1023) ssize_t read;
ptr++;
*ptr = '\0'; while ((read = getline(&line, &buflen, hist)) >= 0) {
if (strlen(buf)) if (read > 0 && line[read - 1] == '\n') {
update_history(history, buf); read--;
else line[read] = '\0';
}
if (read > 0) {
unsunder(line, read);
update_history(history, line);
} else
history = &replace_history; history = &replace_history;
} }
fclose(hist); fclose(hist);
free(buf); free(line);
free(nanohist);
UNSET(HISTORY_CHANGED); 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 */ /* save histories to ~/.nano_history */
void save_history(void) void save_history(void)
{ {
FILE *hist; char *nanohist;
const struct passwd *userage = NULL;
char *nanohist = NULL;
char *homenv = getenv("HOME");
historytype *h;
/* don't save unchanged or empty histories */ /* don't save unchanged or empty histories */
if ((search_history.count == 0 && replace_history.count == 0) || if ((search_history.count == 0 && replace_history.count == 0) ||
!ISSET(HISTORY_CHANGED) || ISSET(VIEW_MODE)) !ISSET(HISTORY_CHANGED) || ISSET(VIEW_MODE))
return; return;
if (homenv != NULL) { nanohist = histfilename();
nanohist = charealloc(nanohist, strlen(homenv) + 15);
sprintf(nanohist, "%s/.nano_history", homenv); if (nanohist != NULL) {
} else { FILE *hist = fopen(nanohist, "wb");
userage = getpwuid(geteuid());
endpwent();
nanohist = charealloc(nanohist, strlen(userage->pw_dir) + 15);
sprintf(nanohist, "%s/.nano_history", userage->pw_dir);
}
if (homenv != NULL || userage != NULL) {
hist = fopen(nanohist, "wb");
if (hist == NULL) if (hist == NULL)
rcfile_error(N_("Error writing %s: %s"), nanohist, strerror(errno)); rcfile_error(N_("Error writing %s: %s"), nanohist, strerror(errno));
else { else {
/* set rw only by owner for security ?? */ /* set rw only by owner for security ?? */
chmod(nanohist, S_IRUSR | S_IWUSR); chmod(nanohist, S_IRUSR | S_IWUSR);
/* write oldest first */
for (h = search_history.tail; h->prev; h = h->prev) { if (!writehist(hist, &search_history) ||
h->data = charealloc(h->data, strlen(h->data) + 2); putc('\n', hist) == EOF ||
strcat(h->data, "\n"); !writehist(hist, &replace_history))
if (fputs(h->data, hist) == EOF) {
rcfile_error(N_("Error writing %s: %s"), nanohist, strerror(errno));
goto come_from;
}
}
if (fputs("\n", hist) == EOF) {
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)); rcfile_error(N_("Error writing %s: %s"), nanohist, strerror(errno));
goto come_from;
}
}
come_from:
fclose(hist); fclose(hist);
} }
free(nanohist); free(nanohist);
......
...@@ -178,6 +178,10 @@ bool curses_ended = FALSE; /* Indicates to statusbar() to simply ...@@ -178,6 +178,10 @@ bool curses_ended = FALSE; /* Indicates to statusbar() to simply
* write to stderr, since endwin() has * write to stderr, since endwin() has
* ended curses mode. */ * 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 length_of_list(const shortcut *s)
{ {
size_t i = 0; size_t i = 0;
...@@ -1188,5 +1192,8 @@ void thanks_for_all_the_fish(void) ...@@ -1188,5 +1192,8 @@ void thanks_for_all_the_fish(void)
free_history(&search_history); free_history(&search_history);
free_history(&replace_history); free_history(&replace_history);
#endif #endif
#ifdef ENABLE_NANORC
free(homedir);
#endif
} }
#endif /* DEBUG */ #endif /* DEBUG */
...@@ -3000,7 +3000,7 @@ void terminal_init(void) ...@@ -3000,7 +3000,7 @@ void terminal_init(void)
disable_flow_control(); disable_flow_control();
} }
int main(int argc, char *argv[]) int main(int argc, char **argv)
{ {
int optchr; int optchr;
int startline = 0; /* Line to try and start at */ int startline = 0; /* Line to try and start at */
......
...@@ -95,8 +95,8 @@ ...@@ -95,8 +95,8 @@
# endif # endif
#endif #endif
/* If no isblank(), strcasecmp(), strncasecmp(), strcasestr(), or /* If no isblank(), strcasecmp(), strncasecmp(), strcasestr(),
* strnlen(), use the versions we have. */ * strnlen(), getdelim(), or getline(), use the versions we have. */
#ifndef HAVE_ISBLANK #ifndef HAVE_ISBLANK
#define isblank is_blank_char #define isblank is_blank_char
#endif #endif
...@@ -117,6 +117,14 @@ ...@@ -117,6 +117,14 @@
#define strnlen nstrnlen #define strnlen nstrnlen
#endif #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 /* 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 * some key definitions are missing, we have to set all of these, and
* all of the special sentinel values below, to different negative * all of the special sentinel values below, to different negative
......
...@@ -142,6 +142,10 @@ extern historyheadtype replace_history; ...@@ -142,6 +142,10 @@ extern historyheadtype replace_history;
extern bool curses_ended; extern bool curses_ended;
#ifdef ENABLE_NANORC
extern char *homedir;
#endif
/* Functions we want available. */ /* Functions we want available. */
/* Public functions in color.c */ /* Public functions in color.c */
...@@ -224,7 +228,9 @@ char *do_browser(const char *inpath); ...@@ -224,7 +228,9 @@ char *do_browser(const char *inpath);
char *do_browse_from(const char *inpath); char *do_browse_from(const char *inpath);
#endif #endif
#if !defined(NANO_SMALL) && defined(ENABLE_NANORC) #if !defined(NANO_SMALL) && defined(ENABLE_NANORC)
char *histfilename(void);
void load_history(void); void load_history(void);
bool writehist(FILE *hist, historyheadtype *histhead);
void save_history(void); void save_history(void);
#endif #endif
...@@ -461,6 +467,12 @@ const char *revstristr(const char *haystack, const char *needle, const ...@@ -461,6 +467,12 @@ const char *revstristr(const char *haystack, const char *needle, const
#ifndef HAVE_STRNLEN #ifndef HAVE_STRNLEN
size_t nstrnlen(const char *s, size_t maxlen); size_t nstrnlen(const char *s, size_t maxlen);
#endif #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 *strstrwrapper(const char *haystack, const char *needle,
const char *start); const char *start);
void nperror(const char *s); void nperror(const char *s);
......
...@@ -101,7 +101,7 @@ const static rcoption rcopts[] = { ...@@ -101,7 +101,7 @@ const static rcoption rcopts[] = {
static bool errors = FALSE; static bool errors = FALSE;
static int lineno = 0; 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 /* 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. */ make the user hit return to continue starting up nano. */
...@@ -648,16 +648,13 @@ void parse_rcfile(FILE *rcstream) ...@@ -648,16 +648,13 @@ void parse_rcfile(FILE *rcstream)
void do_rcfile(void) void do_rcfile(void)
{ {
FILE *rcstream; FILE *rcstream;
const struct passwd *userage;
uid_t euid = geteuid();
char *homenv = getenv("HOME");
#ifdef SYSCONFDIR #ifdef SYSCONFDIR
assert(sizeof(SYSCONFDIR) == strlen(SYSCONFDIR) + 1); assert(sizeof(SYSCONFDIR) == strlen(SYSCONFDIR) + 1);
nanorc = charalloc(sizeof(SYSCONFDIR) + 7); nanorc = SYSCONFDIR "/nanorc";
sprintf(nanorc, "%s/nanorc", SYSCONFDIR);
/* Try to open system nanorc */ /* Try to open system nanorc */
if ((rcstream = fopen(nanorc, "r")) != NULL) { rcstream = fopen(nanorc, "r");
if (rcstream != NULL) {
/* Parse it! */ /* Parse it! */
parse_rcfile(rcstream); parse_rcfile(rcstream);
fclose(rcstream); fclose(rcstream);
...@@ -666,33 +663,38 @@ void do_rcfile(void) ...@@ -666,33 +663,38 @@ void do_rcfile(void)
lineno = 0; lineno = 0;
{
const char *homenv = getenv("HOME");
/* Rely on $HOME, fall back on getpwuid() */ /* Rely on $HOME, fall back on getpwuid() */
if (homenv != NULL) { if (homenv == NULL) {
nanorc = charealloc(nanorc, strlen(homenv) + 10); const struct passwd *userage = getpwuid(geteuid());
sprintf(nanorc, "%s/.nanorc", homenv);
} else { if (userage != NULL)
userage = getpwuid(euid); homenv = userage->pw_dir;
endpwent(); }
homedir = mallocstrcpy(NULL, homenv);
}
if (userage == NULL) { if (homedir == NULL) {
rcfile_error(N_("I can't find my home directory! Wah!")); rcfile_error(N_("I can't find my home directory! Wah!"));
SET(NO_RCFILE); SET(NO_RCFILE);
} else { } else {
nanorc = charealloc(nanorc, strlen(userage->pw_dir) + 9); size_t homelen = strlen(homedir);
sprintf(nanorc, "%s/.nanorc", userage->pw_dir); char *nanorcf = charalloc(homelen + 9);
}
}
if (!ISSET(NO_RCFILE)) { nanorc = nanorcf;
strcpy(nanorcf, homedir);
strcpy(nanorcf + homelen, "/.nanorc");
#if defined(DISABLE_ROOTWRAP) && !defined(DISABLE_WRAPPING) #if defined(DISABLE_ROOTWRAP) && !defined(DISABLE_WRAPPING)
/* If we've already read SYSCONFDIR/nanorc (if it's there), we're /* If we've already read SYSCONFDIR/nanorc (if it's there), we're
root, and --disable-wrapping-as-root is used, turn wrapping off */ root, and --disable-wrapping-as-root is used, turn wrapping off */
if (euid == NANO_ROOT_UID) if (geteuid() == NANO_ROOT_UID)
SET(NO_WRAP); SET(NO_WRAP);
#endif #endif
if ((rcstream = fopen(nanorc, "r")) == NULL) { rcstream = fopen(nanorc, "r");
if (rcstream == NULL) {
/* Don't complain about the file not existing */ /* Don't complain about the file not existing */
if (errno != ENOENT) { if (errno != ENOENT) {
rcfile_error(N_("Error reading %s: %s"), nanorc, strerror(errno)); rcfile_error(N_("Error reading %s: %s"), nanorc, strerror(errno));
...@@ -702,10 +704,10 @@ void do_rcfile(void) ...@@ -702,10 +704,10 @@ void do_rcfile(void)
parse_rcfile(rcstream); parse_rcfile(rcstream);
fclose(rcstream); fclose(rcstream);
} }
free(nanorcf);
} }
lineno = 0; lineno = 0;
free(nanorc);
#ifdef ENABLE_COLOR #ifdef ENABLE_COLOR
set_colorpairs(); set_colorpairs();
#endif #endif
......
...@@ -240,6 +240,65 @@ size_t nstrnlen(const char *s, size_t maxlen) ...@@ -240,6 +240,65 @@ size_t nstrnlen(const char *s, size_t maxlen)
} }
#endif #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 /* If we are searching backwards, we will find the last match that
* starts no later than start. Otherwise we find the first match * starts no later than start. Otherwise we find the first match
* starting no earlier than start. If we are doing a regexp search, we * starting no earlier than start. If we are doing a regexp search, we
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment