From cf388649b9cd6fba38b9c0dca49c29bad3b6f737 Mon Sep 17 00:00:00 2001
From: Arun Prakash Jana <engineerarun@gmail.com>
Date: Thu, 19 Dec 2019 08:37:17 +0530
Subject: [PATCH] Internal states, sessions changes

- replace bools for internal non-persistent binary states with an uchar
- do not store path to session, derive from config path in tmp buffer
- program option to load session is not `-s`
- remove auto-stored session "@" on restore
- minor help update
---
 misc/auto-completion/bash/nnn-completion.bash |   4 +-
 misc/auto-completion/fish/nnn.fish            |   2 +-
 misc/auto-completion/zsh/_nnn                 |   2 +-
 nnn.1                                         |   8 +-
 src/nnn.c                                     | 102 ++++++++++--------
 5 files changed, 64 insertions(+), 54 deletions(-)

diff --git a/misc/auto-completion/bash/nnn-completion.bash b/misc/auto-completion/bash/nnn-completion.bash
index 11b8ef2..22da4af 100644
--- a/misc/auto-completion/bash/nnn-completion.bash
+++ b/misc/auto-completion/bash/nnn-completion.bash
@@ -16,7 +16,6 @@ _nnn ()
         -b
         -c
         -d
-        -e
         -E
         -g
         -H
@@ -28,6 +27,7 @@ _nnn ()
         -Q
         -r
         -R
+        -s
         -S
         -t
         -v
@@ -39,7 +39,7 @@ _nnn ()
         COMPREPLY=( $(compgen -W "$bookmarks" -- "$cur") )
     elif [[ $prev == -p ]]; then
         COMPREPLY=( $(compgen -f -d -- "$cur") )
-    elif [[ $prev == -e ]]; then
+    elif [[ $prev == -s ]]; then
         local sessions_dir=${XDG_CONFIG_HOME:-$HOME/.config}/nnn/sessions
         COMPREPLY=( $(cd "$sessions_dir" && compgen -f -d -- "$cur") )
     elif [[ $cur == -* ]]; then
diff --git a/misc/auto-completion/fish/nnn.fish b/misc/auto-completion/fish/nnn.fish
index 0c10f93..c71d33c 100644
--- a/misc/auto-completion/fish/nnn.fish
+++ b/misc/auto-completion/fish/nnn.fish
@@ -15,7 +15,6 @@ complete -c nnn -s a    -d 'use access time'
 complete -c nnn -s b -r -d 'bookmark key to open' -x -a '(echo $NNN_BMS | awk -F: -v RS=\; \'{print $1"\t"$2}\')'
 complete -c nnn -s c    -d 'cli-only opener'
 complete -c nnn -s d    -d 'start in detail mode'
-complete -c nnn -s e -r -d 'load session by name' -x -a '@\t"last session" (ls $sessions_dir)'
 complete -c nnn -s E    -d 'use EDITOR for undetached edits'
 complete -c nnn -s g    -d 'regex filters'
 complete -c nnn -s H    -d 'show hidden files'
@@ -27,6 +26,7 @@ complete -c nnn -s p -r -d 'copy selection to file' -a '-\tstdout'
 complete -c nnn -s Q    -d 'disable quit confirmation'
 complete -c nnn -s r    -d 'show cp, mv progress (Linux-only)'
 complete -c nnn -s R    -d 'disable rollover at edges'
+complete -c nnn -s s -r -d 'load session by name' -x -a '@\t"last session" (ls $sessions_dir)'
 complete -c nnn -s S    -d 'start in disk usage analyzer mode'
 complete -c nnn -s t    -d 'disable dir auto-select'
 complete -c nnn -s v    -d 'show program version and exit'
diff --git a/misc/auto-completion/zsh/_nnn b/misc/auto-completion/zsh/_nnn
index 6e94a0e..c37012c 100644
--- a/misc/auto-completion/zsh/_nnn
+++ b/misc/auto-completion/zsh/_nnn
@@ -13,7 +13,6 @@ args=(
     '(-b)-b[bookmark key to open]:key char'
     '(-c)-c[cli-only opener]'
     '(-d)-d[start in detail mode]'
-    '(-e)-e[load session]:session name'
     '(-E)-E[use EDITOR for undetached edits]'
     '(-g)-g[regex filters]'
     '(-H)-H[show hidden files]'
@@ -25,6 +24,7 @@ args=(
     '(-Q)-Q[disable quit confirmation]'
     '(-r)-r[show cp, mv progress (Linux-only)]'
     '(-R)-R[disable rollover at edges]'
+    '(-s)-s[load session]:session name'
     '(-S)-S[start in disk usage analyzer mode]'
     '(-t)-t[disable dir auto-select]'
     '(-v)-v[show program version and exit]'
diff --git a/nnn.1 b/nnn.1
index 89daf5d..9b8a806 100644
--- a/nnn.1
+++ b/nnn.1
@@ -10,7 +10,6 @@
 .Op Ar -b key
 .Op Ar -c
 .Op Ar -d
-.Op Ar -e name
 .Op Ar -E
 .Op Ar -g
 .Op Ar -H
@@ -21,6 +20,7 @@
 .Op Ar -Q
 .Op Ar -r
 .Op Ar -R
+.Op Ar -s name
 .Op Ar -S
 .Op Ar -v
 .Op Ar -x
@@ -56,9 +56,6 @@ supports the following options:
 .Fl d
         detail mode
 .Pp
-.Fl "e name"
-        load a session by name
-.Pp
 .Fl E
         use $EDITOR for internal undetached edits
 .Pp
@@ -92,6 +89,9 @@ supports the following options:
 .Fl R
         disable rollover at edges
 .Pp
+.Fl "s name"
+        load a session by name
+.Pp
 .Fl S
         start in disk usage analyzer mode
 .Pp
diff --git a/src/nnn.c b/src/nnn.c
index 20b14ba..a30320b 100644
--- a/src/nnn.c
+++ b/src/nnn.c
@@ -321,7 +321,6 @@ 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;
@@ -331,8 +330,6 @@ static kv bookmark[BM_MAX];
 static kv plug[PLUGIN_MAX];
 static uchar g_tmpfplen;
 static uchar blk_shift = BLK_SHIFT_512;
-static bool interrupted = FALSE;
-static bool rangesel = FALSE;
 
 /* Retain old signal handlers */
 #ifdef __linux__
@@ -353,8 +350,14 @@ static char g_tmpfpath[TMP_LEN_MAX] __attribute__ ((aligned));
 /* Buffer to store plugins control pipe location */
 static char g_pipepath[TMP_LEN_MAX] __attribute__ ((aligned));
 
+/* MISC NON-PERSISTENT INTERNAL BINARY STATES */
+
 /* Plugin control initialization status */
-static bool g_plinit = FALSE;
+#define STATE_PLUGIN_INIT 0x1
+#define STATE_INTERRUPTED 0x2
+#define STATE_RANGESEL 0x4
+
+static uchar g_states;
 
 /* Options to identify file mime */
 #if defined(__APPLE__)
@@ -531,6 +534,7 @@ static const char * const env_cfg[] = {
 #define ENV_EDITOR 2
 #define ENV_PAGER 3
 #define ENV_NCUR 4
+#define DIR_SESSIONS 5
 
 static const char * const envs[] = {
 	"SHELL",
@@ -538,6 +542,7 @@ static const char * const envs[] = {
 	"EDITOR",
 	"PAGER",
 	"nnn",
+	"/sessions",
 };
 
 #ifdef __linux__
@@ -604,7 +609,7 @@ static void sigint_handler(int sig)
 {
 	(void) sig;
 
-	interrupted = TRUE;
+	g_states |= STATE_INTERRUPTED;
 }
 
 static uint xatoi(const char *str)
@@ -2824,9 +2829,17 @@ static void savecurctx(settings *curcfg, char *path, char *curname, int r /* nex
 	*curcfg = cfg;
 }
 
-static void save_session(bool last_session, int *presel)
+static void makesessionpath(char *spath, const char *sname)
+{
+	size_t r = mkpath(cfgdir, envs[DIR_SESSIONS] + 1 /* begins with '/' */, spath);
+
+	spath[r - 1] = '/';
+	xstrlcpy(spath + r, sname, PATH_MAX - r);
+
+}
+
+static void save_session(bool last_session, int *presel, char *spath)
 {
-	char spath[PATH_MAX];
 	int i;
 	session_header_t header;
 	FILE *fsession;
@@ -2850,7 +2863,8 @@ static void save_session(bool last_session, int *presel)
 	sname = !last_session ? xreadline(NULL, messages[MSG_SSN_NAME]) : "@";
 	if (!sname[0])
 		return;
-	mkpath(sessiondir, sname, spath);
+
+	makesessionpath(spath, sname);
 
 	fsession = fopen(spath, "wb");
 	if (!fsession) {
@@ -2884,9 +2898,8 @@ END:
 		printwait(messages[MSG_FAILED], presel);
 }
 
-static bool load_session(const char *sname, char **path, char **lastdir, char **lastname, bool restore)
+static bool load_session(const char *sname, char **path, char **lastdir, char **lastname, char *spath, bool restore)
 {
-	char spath[PATH_MAX];
 	int i = 0;
 	session_header_t header;
 	FILE *fsession;
@@ -2897,13 +2910,13 @@ static bool load_session(const char *sname, char **path, char **lastdir, char **
 		sname = sname ? sname : xreadline(NULL, messages[MSG_SSN_NAME]);
 		if (!sname[0])
 			return FALSE;
+	}
 
-		mkpath(sessiondir, sname, spath);
-	} else
-		mkpath(sessiondir, "@", spath);
-
+	/* Save current session */
 	if (has_loaded_dynamically)
-		save_session(TRUE, NULL);
+		save_session(TRUE, NULL, spath);
+
+	makesessionpath(spath, (!restore ? sname : "@"));
 
 	fsession = fopen(spath, "rb");
 	if (!fsession) {
@@ -2945,7 +2958,8 @@ END:
 	if (!status) {
 		printmsg(messages[MSG_FAILED]);
 		xdelay(XDELAY_INTERVAL_MS);
-	}
+	} else if (restore)
+		unlink(spath);
 
 	return status;
 }
@@ -3474,7 +3488,7 @@ static void show_help(const char *path)
 	    "6(Sh)Tab  Cycle context%-11cd  Detail view toggle\n"
 		  "c/  Filter%-13cIns ^N  Nav-as-you-type toggle\n"
 		"aEsc  Exit prompt%-9c^L F5  Redraw/clear prompt\n"
-		  "c.  Hidden toggle%-11c?  Help, conf\n"
+		  "c.  Toggle hidden%-11c?  Help, conf\n"
 	       "9Q ^Q  Quit%-20cq  Quit context\n"
 		 "b^G  QuitCD%-1c\n"
 		"1FILES\n"
@@ -3606,9 +3620,9 @@ static bool run_selected_plugin(char **path, const char *file, char *newpath, ch
 	if (*file == '_')
 		return run_cmd_as_plugin(*path, file, newpath, runfile);
 
-	if (!g_plinit) {
+	if (!(g_states & STATE_PLUGIN_INIT)) {
 		plctrl_init();
-		g_plinit = TRUE;
+		g_states |= STATE_PLUGIN_INIT;
 	}
 
 	fd = open(g_pipepath, O_RDONLY | O_NONBLOCK);
@@ -3795,7 +3809,7 @@ static int dentfill(char *path, struct entry **dents)
 
 					dir_blocks += dirwalk(buf, &sb);
 
-					if (interrupted) {
+					if (g_states & STATE_INTERRUPTED) {
 						closedir(dirp);
 						return n;
 					}
@@ -3892,7 +3906,7 @@ static int dentfill(char *path, struct entry **dents)
 				else
 					num_files = num_saved;
 
-				if (interrupted) {
+				if (g_states & STATE_INTERRUPTED) {
 					closedir(dirp);
 					return n;
 				}
@@ -4188,7 +4202,7 @@ static void redraw(char *path)
 
 			mvprintw(lastln, 0, "%d/%d [%d:%s] %cu:%s free:%s files:%lu %lldB %s",
 				 cur + 1, ndents, cfg.selmode,
-				 (rangesel ? "*" : (nselected ? xitoa(nselected) : "")),
+				 ((g_states & STATE_RANGESEL) ? "*" : (nselected ? xitoa(nselected) : "")),
 				 c, buf, coolsize(get_fs_info(path, FREE)), num_files,
 				 (ll)pent->blocks << blk_shift, ptr);
 		} else { /* light or detail mode */
@@ -4202,7 +4216,7 @@ static void redraw(char *path)
 
 			mvprintw(lastln, 0, "%d/%d [%d:%s] %s%s %s %s %s [%s]",
 				 cur + 1, ndents, cfg.selmode,
-				 (rangesel ? "*" : (nselected ? xitoa(nselected) : "")),
+				 ((g_states & STATE_RANGESEL) ? "*" : (nselected ? xitoa(nselected) : "")),
 				 sort, buf, get_lsperms(pent->mode), coolsize(pent->size), ptr, base);
 		}
 	} else
@@ -4231,7 +4245,7 @@ static void browse(char *ipath, const char *session)
 	xcols = COLS;
 
 	/* setup first context */
-	if (!session || !load_session(session, &path, &lastdir, &lastname, FALSE)) {
+	if (!session || !load_session(session, &path, &lastdir, &lastname, newpath, FALSE)) {
 		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';
@@ -4279,8 +4293,8 @@ begin:
 		printwarn(&presel);
 
 	populate(path, lastname);
-	if (interrupted) {
-		interrupted = FALSE;
+	if (g_states & STATE_INTERRUPTED) {
+		g_states &= ~STATE_INTERRUPTED;
 		cfg.apparentsz = 0;
 		cfg.blkorder = 0;
 		blk_shift = BLK_SHIFT_512;
@@ -4869,8 +4883,8 @@ nochange:
 				goto nochange;
 
 			startselection();
-			if (rangesel)
-				rangesel = FALSE;
+			if (g_states & STATE_RANGESEL)
+				g_states &= ~STATE_RANGESEL;
 
 			/* Toggle selection status */
 			dents[cur].flags ^= FILE_SELECTED;
@@ -4903,14 +4917,14 @@ nochange:
 				goto nochange;
 
 			startselection();
-			rangesel ^= TRUE;
+			g_states ^= STATE_RANGESEL;
 
 			if (stat(path, &sb) == -1) {
 				printwarn(&presel);
 				goto nochange;
 			}
 
-			if (rangesel) { /* Range selection started */
+			if (g_states & STATE_RANGESEL) { /* Range selection started */
 				inode = sb.st_ino;
 				selstartid = cur;
 				continue;
@@ -4940,8 +4954,8 @@ nochange:
 					goto nochange;
 
 				startselection();
-				if (rangesel)
-					rangesel = FALSE;
+				if (g_states & STATE_RANGESEL)
+					g_states &= ~STATE_RANGESEL;
 
 				selstartid = 0;
 				selendid = ndents - 1;
@@ -5348,12 +5362,13 @@ nochange:
 			r = get_input(messages[MSG_SSN_OPTS]);
 
 			if (r == 's') {
-				save_session(FALSE, &presel);
+				save_session(FALSE, &presel, newpath);
 				goto nochange;
 			}
 
 			if (r == 'l' || r == 'r') {
-				if (load_session(NULL, &path, &lastdir, &lastname, r == 'r')) {
+				if (load_session(NULL, &path, &lastdir,
+						 &lastname, newpath, r == 'r')) {
 					setdirwatch();
 					goto begin;
 				}
@@ -5469,7 +5484,6 @@ 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"
 		" -E      use EDITOR for undetached edits\n"
 		" -g      regex filters [default: string]\n"
 		" -H      show hidden files\n"
@@ -5481,6 +5495,7 @@ static void usage(void)
 		" -Q      no quit confirmation\n"
 		" -r      use advcpmv patched cp, mv\n"
 		" -R      no rollover at edges\n"
+		" -s name load session by name\n"
 		" -S      du mode\n"
 		" -t      no dir auto-select\n"
 		" -v      show version\n"
@@ -5519,8 +5534,7 @@ static bool setup_config(void)
 
 	cfgdir = (char *)malloc(len);
 	plugindir = (char *)malloc(len);
-	sessiondir = (char *)malloc(len);
-	if (!cfgdir || !plugindir || !sessiondir) {
+	if (!cfgdir || !plugindir) {
 		xerror();
 		return FALSE;
 	}
@@ -5554,12 +5568,9 @@ static bool setup_config(void)
 	}
 
 	/* Create ~/.config/nnn/sessions */
-	xstrlcpy(cfgdir + r + 4 - 1, "/sessions", 10); /* subtract length of "/nnn" (4) */
+	xstrlcpy(cfgdir + r + 4 - 1, envs[DIR_SESSIONS], 10); /* subtract length of "/nnn" (4) */
 	DPRINTF_S(cfgdir);
 
-	xstrlcpy(sessiondir, cfgdir, len);
-	DPRINTF_S(sessiondir);
-
 	if (!xmktree(cfgdir, TRUE)) {
 		xerror();
 		return FALSE;
@@ -5604,7 +5615,6 @@ static void cleanup(void)
 {
 	free(g_selpath);
 	free(plugindir);
-	free(sessiondir);
 	free(cfgdir);
 	free(initpath);
 	free(bmstr);
@@ -5627,7 +5637,7 @@ int main(int argc, char *argv[])
 	bool progress = FALSE;
 #endif
 
-	while ((opt = getopt(argc, argv, "HSKiab:cde:Egnop:QrRtvxh")) != -1) {
+	while ((opt = getopt(argc, argv, "HSKiab:cdEgnop:QrRs:tvxh")) != -1) {
 		switch (opt) {
 		case 'S':
 			cfg.blkorder = 1;
@@ -5649,9 +5659,6 @@ int main(int argc, char *argv[])
 		case 'c':
 			cfg.cliopener = 1;
 			break;
-		case 'e':
-			session = optarg;
-			break;
 		case 'E':
 			cfg.waitedit = 1;
 			break;
@@ -5696,6 +5703,9 @@ int main(int argc, char *argv[])
 		case 'R':
 			cfg.rollover = 0;
 			break;
+		case 's':
+			session = optarg;
+			break;
 		case 't':
 			cfg.autoselect = 0;
 			break;