diff options
author | Anna Arad <4895022+annagrram@users.noreply.github.com> | 2019-10-19 01:11:39 +0300 |
---|---|---|
committer | Mischievous Meerkat <engineerarun@gmail.com> | 2019-10-19 03:41:39 +0530 |
commit | 60dac94a5e3727d7c8bf0f02f3d6f8b53f8d1d02 (patch) | |
tree | 3db311f182889799ef16a16c05d87644e84c61ca /src | |
parent | 2da5602a4f7bff17d43c939e7329aab34e2a85f9 (diff) | |
download | nnn-60dac94a5e3727d7c8bf0f02f3d6f8b53f8d1d02.tar.gz |
Implementing sessions support (#360)
* Initial commit of sessions implementation
* Reduce code duplication
* Move load session to program flag -e
* Fix context initialization problem when loading session
* Add pinned directory to session and reduce session file size
* Make load_session print an error if exists and few minor adjustments
* Refactor session's file structure
* Initialize required structures in load_session before loading
* Add load session dynamically, restore last session, and extra fixes
* Fix indentation
* Add sessions documentation to man page
* Update fish completions with sessions and make some improvements
* Move to single keybinding session management and add help info
* ESC when asked to insert session name behaves better
* Add sessions completion for bash
* Remove pinned dir from session and minor code refactors
Diffstat (limited to 'src')
-rw-r--r-- | src/nnn.c | 202 | ||||
-rw-r--r-- | src/nnn.h | 2 |
2 files changed, 187 insertions, 17 deletions
@@ -105,6 +105,7 @@ /* Macro definitions */ #define VERSION "2.7" #define GENERAL_INFO "BSD 2-Clause\nhttps://github.com/jarun/nnn" +#define SESSIONS_VERSION 0 #ifndef S_BLKSIZE #define S_BLKSIZE 512 /* S_BLKSIZE is missing on Android NDK (Termux) */ @@ -254,6 +255,14 @@ typedef struct { uint color; /* Color code for directories */ } context; +typedef struct { + size_t ver; + size_t pathln[CTX_MAX]; + size_t lastln[CTX_MAX]; + size_t nameln[CTX_MAX]; + size_t fltrln[CTX_MAX]; +} session_header_t; + /* GLOBALS */ /* Configuration, contexts */ @@ -305,6 +314,7 @@ static char *initpath; static char *cfgdir; static char *g_selpath; static char *plugindir; +static char *sessiondir; static char *pnamebuf, *pselbuf; static struct entry *dents; static blkcnt_t ent_blocks; @@ -2150,7 +2160,7 @@ static char *getreadline(char *prompt, char *path, char *curpath, int *presel) * Updates out with "dir/name or "/name" * Returns the number of bytes copied including the terminating NULL byte */ -static size_t mkpath(char *dir, char *name, char *out) +static size_t mkpath(const char *dir, const char *name, char *out) { size_t len; @@ -2634,6 +2644,122 @@ static void savecurctx(settings *curcfg, char *path, char *curname, int r /* nex *curcfg = cfg; } +static void save_session(bool last_session, int *presel) +{ + char session_path[PATH_MAX + 1]; + int status = _FAILURE; + int i; + session_header_t header; + char *session_name; + + header.ver = SESSIONS_VERSION; + + for (i = 0; i < CTX_MAX; ++i) { + if (!g_ctx[i].c_cfg.ctxactive) { + header.pathln[i] = header.nameln[i] + = header.lastln[i] = header.fltrln[i] = 0; + } else { + header.pathln[i] = strnlen(g_ctx[i].c_path, PATH_MAX) + 1; + header.nameln[i] = strnlen(g_ctx[i].c_name, NAME_MAX) + 1; + header.lastln[i] = strnlen(g_ctx[i].c_last, PATH_MAX) + 1; + header.fltrln[i] = strnlen(g_ctx[i].c_fltr, REGEX_MAX) + 1; + } + } + + session_name = !last_session ? xreadline("", "session name: ") : "@"; + if (session_name[0] != '\0') + mkpath(sessiondir, session_name, session_path); + else + return; + + FILE *fsession = fopen(session_path, "wb"); + if (!fsession) { + printwait("failed to open session file", presel); + return; + } + + if ((fwrite(&header, sizeof(header), 1, fsession) != 1) + || (fwrite(&cfg, sizeof(cfg), 1, fsession) != 1)) + goto END; + + for (i = 0; i < CTX_MAX; ++i) + if ((fwrite(&g_ctx[i].c_cfg, sizeof(settings), 1, fsession) != 1) + || (fwrite(&g_ctx[i].color, sizeof(uint), 1, fsession) != 1) + || (header.nameln[i] > 0 && fwrite(g_ctx[i].c_name, header.nameln[i], 1, fsession) != 1) + || (header.lastln[i] > 0 && fwrite(g_ctx[i].c_last, header.lastln[i], 1, fsession) != 1) + || (header.fltrln[i] > 0 && fwrite(g_ctx[i].c_fltr, header.fltrln[i], 1, fsession) != 1) + || (header.pathln[i] > 0 && fwrite(g_ctx[i].c_path, header.pathln[i], 1, fsession) != 1)) + goto END; + + status = _SUCCESS; + +END: + fclose(fsession); + + if (status == _FAILURE) + printwait("failed to write session data", presel); +} + +static bool load_session(const char *session_name, char **path, char **lastdir + , char **lastname, bool restore_session) { + char session_path[PATH_MAX + 1]; + int status = _FAILURE; + int i = 0; + session_header_t header; + bool has_loaded_dynamically = !(session_name || restore_session); + + if (!restore_session) { + session_name = session_name ? session_name : xreadline("", "session name: "); + if (session_name[0] != '\0') + mkpath(sessiondir, session_name ? session_name : xreadline("", "session name: "), session_path); + else + return _FAILURE; + } else + mkpath(sessiondir, "@", session_path); + + if (has_loaded_dynamically) + save_session(TRUE, NULL); + + FILE *fsession = fopen(session_path, "rb"); + if (!fsession) { + printmsg("failed to open session file"); + xdelay(); + return _FAILURE; + } + + if ((fread(&header, sizeof(header), 1, fsession) != 1) + || (header.ver != SESSIONS_VERSION) + || (fread(&cfg, sizeof(cfg), 1, fsession) != 1)) + goto END; + + g_ctx[cfg.curctx].c_name[0] = g_ctx[cfg.curctx].c_last[0] + = g_ctx[cfg.curctx].c_fltr[0] = g_ctx[cfg.curctx].c_fltr[1] = '\0'; + + for (; i < CTX_MAX; ++i) + if ((fread(&g_ctx[i].c_cfg, sizeof(settings), 1, fsession) != 1) + || (fread(&g_ctx[i].color, sizeof(uint), 1, fsession) != 1) + || (header.nameln[i] > 0 && fread(g_ctx[i].c_name, header.nameln[i], 1, fsession) != 1) + || (header.lastln[i] > 0 && fread(g_ctx[i].c_last, header.lastln[i], 1, fsession) != 1) + || (header.fltrln[i] > 0 && fread(g_ctx[i].c_fltr, header.fltrln[i], 1, fsession) != 1) + || (header.pathln[i] > 0 && fread(g_ctx[i].c_path, header.pathln[i], 1, fsession) != 1)) + goto END; + + *path = g_ctx[cfg.curctx].c_path; + *lastdir = g_ctx[cfg.curctx].c_last; + *lastname = g_ctx[cfg.curctx].c_name; + status = _SUCCESS; + +END: + fclose(fsession); + + if (status == _FAILURE) { + printmsg("failed to read session data"); + xdelay(); + } + + return status; +} + /* * Gets only a single line (that's what we need * for now) or shows full command output in pager. @@ -3078,8 +3204,9 @@ static void show_help(const char *path) "cA Apparent du S du\n" "cs Size E Extn t Time\n" "1MISC\n" - "9! ^] Shell = Launch C Execute entry\n" + "9! ^] Shell C Execute entry\n" "9R ^V Pick plugin :K xK Execute plugin K\n" + "cU Manage session = Launch\n" "cc SSHFS mount u Unmount\n" "b^P Prompt/run cmd L Lock\n"}; @@ -3620,7 +3747,7 @@ static void redraw(char *path) printmsg("0/0"); } -static void browse(char *ipath) +static void browse(char *ipath, const char *session) { char newpath[PATH_MAX] __attribute__ ((aligned)); char mark[PATH_MAX] __attribute__ ((aligned)); @@ -3639,17 +3766,23 @@ static void browse(char *ipath) atexit(dentfree); + xlines = LINES; + xcols = COLS; + /* setup first context */ - xstrlcpy(g_ctx[0].c_path, ipath, PATH_MAX); /* current directory */ - path = g_ctx[0].c_path; - g_ctx[0].c_last[0] = g_ctx[0].c_name[0] = newpath[0] = mark[0] = '\0'; - rundir[0] = runfile[0] = '\0'; - lastdir = g_ctx[0].c_last; /* last visited directory */ - lastname = g_ctx[0].c_name; /* last visited filename */ - g_ctx[0].c_fltr[0] = g_ctx[0].c_fltr[1] = '\0'; - g_ctx[0].c_cfg = cfg; /* current configuration */ + if (!session || load_session(session, &path, &lastdir, &lastname, FALSE) == _FAILURE) { + xstrlcpy(g_ctx[0].c_path, ipath, PATH_MAX); /* current directory */ + path = g_ctx[0].c_path; + g_ctx[0].c_last[0] = g_ctx[0].c_name[0] = '\0'; + lastdir = g_ctx[0].c_last; /* last visited directory */ + lastname = g_ctx[0].c_name; /* last visited filename */ + g_ctx[0].c_fltr[0] = g_ctx[0].c_fltr[1] = '\0'; + g_ctx[0].c_cfg = cfg; /* current configuration */ + } + + newpath[0] = rundir[0] = runfile[0] = mark[0] = '\0'; - cfg.filtermode ? (presel = FILTER) : (presel = 0); + presel = cfg.filtermode ? FILTER : 0; dents = xrealloc(dents, total_dents * sizeof(struct entry)); if (!dents) @@ -4878,6 +5011,22 @@ nochange: } } return; + case SEL_SESSIONS: + r = get_input("'s'(ave) / 'l'(oad) / 'r'(estore) session?"); + + if (r == 's') { + save_session(FALSE, &presel); + goto nochange; + } else if (r == 'l' || r == 'r') { + if (load_session(NULL, &path, &lastdir, &lastname, r == 'r') == _SUCCESS) { + setdirwatch(); + goto begin; + } + + presel = MSGWAIT; + goto nochange; + } + break; default: if (xlines != LINES || xcols != COLS) { idle = 0; @@ -4929,6 +5078,7 @@ static void usage(void) " -b key open bookmark key\n" " -c cli-only opener\n" " -d detail mode\n" + " -e name load session by name\n" " -f run filter as cmd on prompt key\n" " -H show hidden files\n" " -i nav-as-you-type mode\n" @@ -4966,16 +5116,17 @@ static bool setup_config(void) return FALSE; } - len = strlen(xdgcfg) + 1 + 12; /* add length of "/nnn/plugins" */ + len = strlen(xdgcfg) + 1 + 13; /* add length of "/nnn/sessions" */ xdg = TRUE; } if (!xdg) - len = strlen(home) + 1 + 20; /* add length of "/.config/nnn/plugins" */ + len = strlen(home) + 1 + 21; /* add length of "/.config/nnn/sessions" */ cfgdir = (char *)malloc(len); plugindir = (char *)malloc(len); - if (!cfgdir || !plugindir) { + sessiondir = (char *)malloc(len); + if (!cfgdir || !plugindir || !sessiondir) { xerror(); return FALSE; } @@ -5017,6 +5168,18 @@ static bool setup_config(void) return FALSE; } + /* Create ~/.config/nnn/sessions */ + xstrlcpy(cfgdir + r + 4 - 1, "/sessions", 10); + DPRINTF_S(cfgdir); + + xstrlcpy(sessiondir, cfgdir, len); + DPRINTF_S(sessiondir); + + if (!create_dir(cfgdir)) { + xerror(); + return FALSE; + } + /* Reset to config path */ cfgdir[r + 3] = '\0'; DPRINTF_S(cfgdir); @@ -5056,6 +5219,7 @@ static void cleanup(void) { free(g_selpath); free(plugindir); + free(sessiondir); free(cfgdir); free(initpath); free(bmstr); @@ -5070,12 +5234,13 @@ int main(int argc, char *argv[]) { mmask_t mask; char *arg = NULL; + char *session = NULL; int opt; #ifdef __linux__ bool progress = FALSE; #endif - while ((opt = getopt(argc, argv, "HSKiab:cdfnop:rstvh")) != -1) { + while ((opt = getopt(argc, argv, "HSKiab:cde:fnop:rstvh")) != -1) { switch (opt) { case 'S': cfg.blkorder = 1; @@ -5097,6 +5262,9 @@ int main(int argc, char *argv[]) case 'c': cfg.cliopener = 1; break; + case 'e': + session = optarg; + break; case 'f': cfg.filtercmd = 1; break; @@ -5348,7 +5516,7 @@ int main(int argc, char *argv[]) if (!initcurses(&mask)) return _FAILURE; - browse(initpath); + browse(initpath, session); mousemask(mask, NULL); exitcurses(); @@ -106,6 +106,7 @@ enum action { SEL_QUITCD, SEL_QUIT, SEL_CLICK, + SEL_SESSIONS, }; /* Associate a pressed key to an action */ @@ -269,4 +270,5 @@ static struct key bindings[] = { { 'Q', SEL_QUIT }, { CONTROL('Q'), SEL_QUIT }, { KEY_MOUSE, SEL_CLICK }, + { 'U', SEL_SESSIONS }, }; |