diff --git a/README.md b/README.md index 0d121f9f..d714e88c 100644 --- a/README.md +++ b/README.md @@ -96,6 +96,7 @@ It runs on Linux, macOS, Raspberry Pi, BSD, Cygwin, Linux subsystem for Windows - Create, rename files and directories - Select files across directories - Copy, move, delete selection + - Create sym/hard link(s) to selection - Transfer files using lftp - Batch rename/move/delete (needs vidir) - Show directories in custom color (default: blue) @@ -226,7 +227,7 @@ Press ? in `nnn` to see the list anytime. ^G Quit and cd q Quit context Q, ^Q Quit ? Help, config FILES - ^O Open with... n Create new + ^O Open with... n Create new/link D File details ^R Rename entry ⎵, ^K Copy entry path r Open dir in vidir Y, ^Y Toggle selection y List selection diff --git a/nnn.1 b/nnn.1 index c5a16471..b7f2ca52 100644 --- a/nnn.1 +++ b/nnn.1 @@ -89,7 +89,7 @@ FILES .It Ic ^O Open with an application (takes 1 combined argument) .It Ic n -Create a new file or directory +Create a new file, directory or link(s) to selection .It Ic D Show entry details .It Ic ^R diff --git a/src/nnn.c b/src/nnn.c index 74272123..90c4ceee 100644 --- a/src/nnn.c +++ b/src/nnn.c @@ -1469,7 +1469,6 @@ END: clearprompt(); buf[len] = '\0'; - DPRINTF_S(buf); wcstombs(g_buf + ((NAME_MAX + 1) << 2), buf, NAME_MAX); return g_buf + ((NAME_MAX + 1) << 2); } @@ -1496,6 +1495,42 @@ static size_t mkpath(char *dir, char *name, char *out, size_t n) return (xstrlcpy(out + len, name, n - len) + len); } +/* Create symbolic/hard link(s) to file(s) in selection list */ +static int xlink(char *suffix, char *path, char *buf, int type) +{ + int count = 0; + char *pbuf = pcopybuf, *fname; + ssize_t pos = 0, len, r; + int (*link_fn)(const char *, const char *) = NULL; + + /* Check if selection is empty */ + if (!copybufpos) + return 0; + + if (type == 's') /* symbolic link */ + link_fn = &symlink; + else if (type == 'h') /* hard link */ + link_fn = &link; + else + return -1; + + while (pos < copybufpos) { + len = strlen(pbuf); + fname = xbasename(pbuf); + r = mkpath(path, fname, buf, PATH_MAX); + xstrlcpy(buf + r - 1, suffix, PATH_MAX - r - 1); + + r = link_fn(pbuf, buf); + if (!r) + ++count; + + pos += len + 1; + pbuf += len + 1; + } + + return count; +} + static bool parsebmstr() { int i = 0; @@ -2105,7 +2140,7 @@ static bool show_help(char *path) "d^G Quit and cd q Quit context\n" "aQ, ^Q Quit ? Help, config\n" "1FILES\n" - "d^O Open with... n Create new\n" + "d^O Open with... n Create new/link\n" "eD File details ^R Rename entry\n" "a⎵, ^K Copy entry path r Open dir in vidir\n" "aY, ^Y Toggle selection y List selection\n" @@ -3334,7 +3369,7 @@ nochange: tmp = xreadline(NULL, "open with: "); break; case SEL_NEW: - tmp = xreadline(NULL, "name: "); + tmp = xreadline(NULL, "name/link suffix: "); break; default: /* SEL_RENAME */ tmp = xreadline(dents[cur].name, ""); @@ -3426,12 +3461,26 @@ nochange: } } else { /* Check if it's a dir or file */ - r = get_input("press 'f'(ile) or 'd'(ir)"); + r = get_input("create 'f'(ile) / 'd'(ir) / 's'(ym) / 'h'(ard)?"); if (r == 'f') { r = openat(fd, tmp, O_CREAT, 0666); close(r); } else if (r == 'd') { r = mkdirat(fd, tmp, 0777); + } else if (r == 's' || r == 'h') { + r = xlink(tmp, path, newpath, r); + close(fd); + + if (r <= 0) { + printmsg("none created"); + goto nochange; + } + + if (cfg.filtermode) + presel = FILTER; + if (ndents) + copycurname(); + goto begin; } else { close(fd); break;