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
- 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)
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)
- 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
- 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,
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,,
#endif /* !DISABLE_BROWSER */
#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;
- const struct passwd *userage = NULL;
- static char *nanohist;
- char *buf, *ptr;
- char *homenv = getenv("HOME");
- historyheadtype *history = &search_history;
+ char *nanohist = NULL;
+ if (homedir != NULL) {
+ size_t homelen = strlen(homedir);
- 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 = charalloc(homelen + 15);
+ strcpy(nanohist, homedir);
+ strcpy(nanohist + homelen, "/.nano_history");
}
+ return nanohist;
+}
+
+void load_history(void)
+{
+ 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. */
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);
* 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;
free_history(&search_history);
free_history(&replace_history);
#endif
+#ifdef ENABLE_NANORC
+ free(homedir);
+#endif
}
#endif /* DEBUG */
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 */
# 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
#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
extern bool curses_ended;
+#ifdef ENABLE_NANORC
+extern char *homedir;
+#endif
+
/* Functions we want available. */
/* Public functions in color.c */
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
#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);
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. */
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);
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));
parse_rcfile(rcstream);
fclose(rcstream);
}
+ free(nanorcf);
}
lineno = 0;
- free(nanorc);
#ifdef ENABLE_COLOR
set_colorpairs();
#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
* starts no later than start. Otherwise we find the first match
* starting no earlier than start. If we are doing a regexp search, we