README.md | 11 ++++++++--- config.def.h | 2 +- nnn.c | 121 ++++++++++++++++++++++++++++++++++++----------------- diff --git a/README.md b/README.md index 47f35fffb4af491ebf652435216a82d23f62ef18..c6d40673e6576fded3f1b89beac3a6b37045a559 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,7 @@ - [Quickstart](#quickstart) - [How to](#how-to) - [cd on quit](#cd-on-quit) - [Copy current file path to clipboard](#copy-current-file-path-to-clipboard) + - [Boost chdir prompt](#boost-chdir-prompt) - [Change file associations](#change-file-associations) - [Why fork?](#why-fork) - [Developers](#developers) @@ -54,12 +55,12 @@ ### Features - Super-easy navigation with roll-over at edges -- Jump HOME or back to the last visited directory (as you normally do!) +- Jump HOME or back to the last visited directory (as usual!) +- Jump to initial dir, chdir prompt, cd ..... (with . as PWD) - Desktop opener integration to handle mime types - Customizable bash script nlay to handle known file types - Disk usage analyzer mode -- Basic and detail views -- Show stat and file information +- Basic and detail view (with stat and file information) - Show media information (needs mediainfo) - Sort by modification time, size - Sort numeric names in numeric order (1, 2, ... 10, 11, ...) @@ -259,6 +260,10 @@ export NNN_COPIER="/home/vaio/copier.sh" Start nnn and use `^K` to copy the absolute path (from `/`) of the file under the cursor to clipboard. + +#### Boost chdir prompt + +nnn uses libreadline for the chdir prompt input. So all the fantastic features of readline (e.g. case insensitive tab completion, history, reverse-i-search) is available to you based on your readline [configuration](https://cnswww.cns.cwru.edu/php/chet/readline/readline.html#SEC9). #### Change file associations diff --git a/config.def.h b/config.def.h index 1b53f6b72019e375f91ef9987ebe039f20b09c15..e23fe3c374f18b955545fd1bb002d0a99922df48 100644 --- a/config.def.h +++ b/config.def.h @@ -66,7 +66,7 @@ { '~', SEL_CDHOME, "", "" }, /* Initial directory */ { '&', SEL_CDBEGIN, "", "" }, /* Last visited dir */ - { '-', SEL_LAST, "", "" }, + { '-', SEL_CDLAST, "", "" }, /* Toggle hide .dot files */ { '.', SEL_TOGGLEDOT, "", "" }, /* Detailed listing */ diff --git a/nnn.c b/nnn.c index 00f7d9878800007dc1f11204386ad000f44fe049..031ffe2d25eaa883c8ab417ccc645c08ceb86921 100644 --- a/nnn.c +++ b/nnn.c @@ -90,7 +90,7 @@ SEL_END, SEL_CD, SEL_CDHOME, SEL_CDBEGIN, - SEL_LAST, + SEL_CDLAST, SEL_TOGGLEDOT, SEL_DETAIL, SEL_STATS, @@ -386,22 +386,18 @@ return (int) (TOUPPER(*s1) - TOUPPER(*s2)); } -/* Trim all white space from both ends */ +/* Trim all whitespace from both ends, / from end */ static char * strstrip(char *s) { - size_t size; - char *end; - - size = strlen(s); - - if (!size) + if (!s || !*s) return s; - end = s + size - 1; - while (end >= s && isspace(*end)) - end--; - *(end + 1) = '\0'; + size_t len = strlen(s) - 1; + + while (len != 0 && (isspace(s[len]) || s[len] == '/')) + len--; + s[len + 1] = '\0'; while (*s && isspace(*s)) s++; @@ -1281,10 +1277,10 @@ int r, fd; enum action sel = SEL_RUNARG + 1; xstrlcpy(path, ipath, sizeof(path)); - xstrlcpy(lastdir, ipath, sizeof(lastdir)); xstrlcpy(fltr, ifilter, sizeof(fltr)); oldpath[0] = '\0'; newpath[0] = '\0'; + lastdir[0] = '\0'; /* Can't move back from initial directory */ begin: if (sel == SEL_GOIN && S_ISDIR(sb.st_mode)) @@ -1471,56 +1467,66 @@ cur = ndents - 1; break; case SEL_CD: { - /* Read target dir */ + static char *tmp, *input; + static int truecd; + + /* Save the program start dir */ tmp = getcwd(newpath, PATH_MAX); if (tmp == NULL) { printwarn(); goto nochange; } + /* Switch to current path for readline(3) */ if (chdir(path) == -1) { printwarn(); goto nochange; } exitcurses(); - char *tmp = readline("chdir: "); + tmp = readline("chdir: "); initcurses(); + /* Change back to program start dir */ if (chdir(newpath) == -1) printwarn(); - - /* Save current */ - if (ndents > 0) - mkpath(path, dents[cur].name, oldpath, sizeof(oldpath)); if (tmp[0] == '\0') break; else + /* Add to readline(3) history */ add_history(tmp); - char *input = tmp; + input = tmp; tmp = strstrip(tmp); if (tmp[0] == '\0') { free(input); break; } + truecd = 0; + if (tmp[0] == '~') { + /* Expand ~ to HOME absolute path */ char *home = getenv("HOME"); if (home) - snprintf(newpath, PATH_MAX, - "%s%s", home, tmp + 1); - else - mkpath(path, tmp, newpath, sizeof(newpath)); + snprintf(newpath, PATH_MAX, "%s%s", home, tmp + 1); + else { + free(input); + break; + } + } else if (tmp[0] == '-' && tmp[1] == '\0') { + if (lastdir[0] == '\0') { + free(input); + break; + } - oldpath[0] = '\0'; - } else if (tmp[0] == '-' && tmp[1] == '\0') { + /* Switch to last visited dir */ xstrlcpy(newpath, lastdir, sizeof(newpath)); - oldpath[0] = '\0'; - + truecd = 1; } else if ((r = all_dots(tmp))) { if (r == 1) { + /* Always in the current dir */ free(input); break; } @@ -1529,15 +1535,17 @@ r--; dir = path; for (fd = 0; fd < r; fd++) { - /* There is no going back */ + /* Reached / ? */ if (strcmp(path, "/") == 0 || strchr(path, '/') == NULL) { + /* If it's a cd .. at / */ if (fd == 0) { printmsg("You are at /"); free(input); goto nochange; } + /* Can't cd beyond / anyway */ break; } else { dir = xdirname(dir); @@ -1549,28 +1557,44 @@ } } } - /* Save history */ - xstrlcpy(oldpath, path, sizeof(oldpath)); + truecd = 1; + + /* Save the path in case of cd .. + We mark the current dir in parent dir */ + if (r == 1) { + xstrlcpy(oldpath, path, sizeof(oldpath)); + truecd = 2; + } + xstrlcpy(newpath, dir, sizeof(newpath)); - } else { + } else mkpath(path, tmp, newpath, sizeof(newpath)); - oldpath[0] = '\0'; - } if (canopendir(newpath) == 0) { - /* Save current */ - if (ndents > 0) - mkpath(path, dents[cur].name, oldpath, sizeof(oldpath)); - printwarn(); free(input); break; } + if (truecd == 0) { + /* Probable change in dir */ + /* No-op if it's the same directory */ + if (strcmp(path, newpath) == 0) { + free(input); + break; + } + + oldpath[0] = '\0'; + } else if (truecd == 1) + /* Sure change in dir */ + oldpath[0] = '\0'; + /* Save last working directory */ xstrlcpy(lastdir, path, sizeof(lastdir)); + /* Save the newly opted dir in path */ xstrlcpy(path, newpath, sizeof(path)); + /* Reset filter */ xstrlcpy(fltr, ifilter, sizeof(fltr)); DPRINTF_S(path); @@ -1583,15 +1607,20 @@ if (tmp == NULL) { clearprompt(); goto nochange; } + if (canopendir(tmp) == 0) { printwarn(); goto nochange; } + if (strcmp(path, tmp) == 0) + break; + /* Save last working directory */ xstrlcpy(lastdir, path, sizeof(lastdir)); xstrlcpy(path, tmp, sizeof(path)); + oldpath[0] = '\0'; /* Reset filter */ xstrlcpy(fltr, ifilter, sizeof(fltr)); DPRINTF_S(path); @@ -1602,19 +1631,33 @@ printwarn(); goto nochange; } + if (strcmp(path, ipath) == 0) + break; + /* Save last working directory */ xstrlcpy(lastdir, path, sizeof(lastdir)); xstrlcpy(path, ipath, sizeof(path)); + oldpath[0] = '\0'; /* Reset filter */ xstrlcpy(fltr, ifilter, sizeof(fltr)); DPRINTF_S(path); goto begin; - case SEL_LAST: + case SEL_CDLAST: + if (lastdir[0] == '\0') + break; + + if (canopendir(lastdir) == 0) { + printwarn(); + goto nochange; + } + xstrlcpy(newpath, lastdir, sizeof(newpath)); xstrlcpy(lastdir, path, sizeof(lastdir)); xstrlcpy(path, newpath, sizeof(path)); oldpath[0] = '\0'; + /* Reset filter */ + xstrlcpy(fltr, ifilter, sizeof(fltr)); DPRINTF_S(path); goto begin; case SEL_TOGGLEDOT: