diff --git a/src/history.c b/src/history.c index 11cf6c20..e3ec9ee9 100644 --- a/src/history.c +++ b/src/history.c @@ -24,6 +24,18 @@ #include #include +#ifndef XDG_DATA_FALLBACK +#define XDG_DATA_FALLBACK "/.local/share/nano" +#endif + +#ifndef SEARCH_HISTORY +#define SEARCH_HISTORY "search_history" +#endif + +#ifndef POSITION_HISTORY +#define POSITION_HISTORY "filepos_history" +#endif + #ifndef DISABLE_HISTORIES static bool history_changed = FALSE; /* Whether any of the history lists has changed. */ @@ -216,27 +228,35 @@ char *get_history_completion(filestruct **h, char *s, size_t len) } #endif /* ENSABLE_TABCOMP */ -/* Return a dynamically-allocated path that is the concatenation of the - * user's home directory and the given name. */ -char *construct_filename(const char *name) +char *concat_string(const char *str1, const char *str2) { - size_t homelen = strlen(homedir); - char *joined = charalloc(homelen + strlen(name) + 1); + size_t str1len = strlen(str1); + char *joined = charalloc(str1len + strlen(str2) + 1); - strcpy(joined, homedir); - strcpy(joined + homelen, name); + strcpy(joined, str1); + strcpy(joined + str1len, str2); return joined; } -char *histfilename(void) -{ - return construct_filename("/.nano/search_history"); -} - -char *poshistfilename(void) +char *historyfilename(const char *historyname) { - return construct_filename("/.nano/filepos_history"); + char *filename; + char *nanodir; + if(have_dotnano()) { + nanodir = concat_string(homedir, "/.nano/"); + } else { + char *xdgdatadir = getenv("XDG_DATA_HOME"); + if (xdgdatadir != NULL) { + nanodir = concat_string(xdgdatadir, "/nano/"); + free(xdgdatadir); + } else { + nanodir = concat_string (homedir, XDG_DATA_FALLBACK "/"); + } + } + filename = concat_string(nanodir, historyname); + free(nanodir); + return filename; } void history_error(const char *msg, ...) @@ -252,38 +272,66 @@ void history_error(const char *msg, ...) ; } -/* Check whether the ~/.nano subdirectory for history files exists. Return - * TRUE if it exists or was successfully created, and FALSE otherwise. */ +/* Check whether the ~/.nano subdirectory for history files exists. */ bool have_dotnano(void) +{ + bool retval = FALSE; + if (homedir == NULL) + return retval; + + struct stat dirstat; + char *nanodir = concat_string(homedir, "/.nano"); + + if (stat(nanodir, &dirstat) != 0 && S_ISDIR(dirstat.st_mode)) + retval = TRUE; + + free(nanodir); + return retval; +} + +/* Check whether the nano data directory (whose location is determined by the + * XDG base dir spec) used for history files exists. + * Return TRUE if it exists or was successfully created, + * and FALSE otherwise. */ +bool have_or_created_xdgdata_nano(void) { bool retval = TRUE; + char *xdgdatadir = getenv("XDG_DATA_HOME"); + char *nanodatadir; + + if (xdgdatadir != NULL) { + nanodatadir = concat_string(xdgdatadir, "/nano"); + } else { + nanodatadir = concat_string(homedir, XDG_DATA_FALLBACK); + } + free(xdgdatadir); + struct stat dirstat; - char *nanodir = construct_filename("/.nano"); - if (stat(nanodir, &dirstat) == -1) { - if (mkdir(nanodir, S_IRWXU | S_IRWXG | S_IRWXO) == -1) { + if (stat(nanodatadir, &dirstat) == -1) { + if (mkdir(nanodatadir, 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)); + nanodatadir, strerror(errno)); retval = FALSE; } } 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); + nanodatadir); retval = FALSE; } - free(nanodir); + free(nanodatadir); return retval; } /* Load the histories for Search and Replace and Execute Command. */ void load_history(void) { - char *histname = histfilename(); + char *histname = historyfilename(SEARCH_HISTORY); FILE *hisfile = fopen(histname, "rb"); if (hisfile == NULL) { @@ -356,7 +404,7 @@ void save_history(void) if (!history_changed) return; - histname = histfilename(); + histname = historyfilename(SEARCH_HISTORY); hisfile = fopen(histname, "wb"); if (hisfile == NULL) @@ -381,7 +429,7 @@ void save_history(void) /* Load the recorded cursor positions for files that were edited. */ void load_poshistory(void) { - char *poshist = poshistfilename(); + char *poshist = historyfilename(POSITION_HISTORY); FILE *hisfile = fopen(poshist, "rb"); if (hisfile == NULL) { @@ -447,7 +495,7 @@ void load_poshistory(void) /* Save the recorded cursor positions for files that were edited. */ void save_poshistory(void) { - char *poshist = poshistfilename(); + char *poshist = historyfilename(POSITION_HISTORY); poshiststruct *posptr; FILE *hisfile = fopen(poshist, "wb"); diff --git a/src/nano.c b/src/nano.c index b3888d9d..564dee72 100644 --- a/src/nano.c +++ b/src/nano.c @@ -2362,7 +2362,7 @@ int main(int argc, char **argv) * directory and its .nano subdirctory exist. */ if (ISSET(HISTORYLOG) || ISSET(POS_HISTORY)) { get_homedir(); - if (homedir == NULL || !have_dotnano()) { + if (!have_dotnano() && !have_or_created_xdgdata_nano()) { UNSET(HISTORYLOG); UNSET(POS_HISTORY); } diff --git a/src/proto.h b/src/proto.h index 7dfcccc3..439887ce 100644 --- a/src/proto.h +++ b/src/proto.h @@ -365,6 +365,8 @@ void get_history_newer_void(void); char *get_history_completion(filestruct **h, char *s, size_t len); #endif bool have_dotnano(void); +bool have_or_created_xdgdata_nano(void); +bool create_xdgdata_nano_directory_if_not_existing(char *nanodir); void load_history(void); void save_history(void); void load_poshistory(void); diff --git a/src/rcfile.c b/src/rcfile.c index 2988d4fc..d82b2c2f 100644 --- a/src/rcfile.c +++ b/src/rcfile.c @@ -31,7 +31,7 @@ #ifdef ENABLE_NANORC #ifndef RCFILE_NAME -#define RCFILE_NAME ".nanorc" +#define RCFILE_NAME "nanorc" #endif static const rcoption rcopts[] = { @@ -511,19 +511,21 @@ void parse_binding(char *ptr, bool dobind) free(keycopy); } -/* Verify that the given file is not a folder nor a device. */ +/* Verify that the given file exists, is not a folder nor a device. */ bool is_good_file(char *file) { struct stat rcinfo; - /* If the thing exists, it may not be a directory nor a device. */ + if (access(file, R_OK) != 0) + return FALSE; /* file does not exist or is not readable */ if (stat(file, &rcinfo) != -1 && (S_ISDIR(rcinfo.st_mode) || S_ISCHR(rcinfo.st_mode) || S_ISBLK(rcinfo.st_mode))) { rcfile_error(S_ISDIR(rcinfo.st_mode) ? _("\"%s\" is a directory") : _("\"%s\" is a device file"), file); + /* If the thing exists, it may not be a directory nor a device. */ return FALSE; - } else - return TRUE; + } + return TRUE; } #ifndef DISABLE_COLOR @@ -1219,6 +1221,16 @@ void parse_one_nanorc(void) rcfile_error(N_("Error reading %s: %s"), nanorc, strerror(errno)); } +bool try_parse_nanorc(char *path, char *name, char *format, int formatlen) { + nanorc = charealloc(nanorc, strlen(path) + strlen(name) + formatlen); + sprintf(nanorc, format, path, name); + if(is_good_file(nanorc)) { + parse_one_nanorc(); + return true; + } + return false; +} + /* First read the system-wide rcfile, then the user's rcfile. */ void do_rcfiles(void) { @@ -1234,18 +1246,32 @@ void do_rcfiles(void) SET(NO_WRAP); #endif + /* Process the current user's nanorc. */ get_homedir(); - if (homedir == NULL) - rcfile_error(N_("I can't find my home directory! Wah!")); - else { - nanorc = charealloc(nanorc, strlen(homedir) + strlen(RCFILE_NAME) + 2); - sprintf(nanorc, "%s/%s", homedir, RCFILE_NAME); + bool dir_found = false; - /* Process the current user's nanorc. */ - parse_one_nanorc(); + /* try $HOME/.nanorc */ + if (homedir != NULL) + dir_found = try_parse_nanorc(homedir, RCFILE_NAME, "%s/.%s", 3); + + /* try $XDG_CONFIG_HOME/nano/nanorc if set */ + if (!dir_found) { + char *xdgconfdir = getenv("XDG_CONFIG_HOME"); + if (xdgconfdir != NULL) { + dir_found = try_parse_nanorc(xdgconfdir, RCFILE_NAME, "%s/nano/%s", 7); + free(xdgconfdir); + } } + /* try .config/nano/nanorc */ + if (homedir != NULL && !dir_found) + dir_found = try_parse_nanorc(homedir, RCFILE_NAME, "%s/.config/nano/%s", 15); + + /* none of the three options worked out */ + if (homedir == NULL && !dir_found) + rcfile_error(N_("I can't find my home directory! Wah!")); + check_vitals_mapped(); free(nanorc);