nlay - a highly customizable file handler

This commit is contained in:
Arun Prakash Jana 2017-04-23 23:12:54 +05:30
parent ff18d580c5
commit 00aaee9ff1
No known key found for this signature in database
GPG key ID: A75979F35C080412
7 changed files with 225 additions and 96 deletions

View file

@ -6,17 +6,18 @@ MANPREFIX = $(PREFIX)/share/man
CFLAGS += -O3 -march=native -Wall -Wextra -Wno-unused-parameter CFLAGS += -O3 -march=native -Wall -Wextra -Wno-unused-parameter
LDLIBS = -lreadline LDLIBS = -lreadline
ifeq ($(shell uname), Darwin) ifeq ($(shell uname), Darwin)
LDLIBS += -lncurses LDLIBS += -lncurses
else else
LDLIBS += -lncursesw LDLIBS += -lncursesw
endif endif
DISTFILES = nnn.c config.def.h nnn.1 Makefile README.md LICENSE DISTFILES = nlay nnn.c config.def.h nnn.1 Makefile README.md LICENSE
LOCALCONFIG = config.h LOCALCONFIG = config.h
SRC = nnn.c SRC = nnn.c
BIN = nnn BIN = nnn
PLAYER = nlay
all: $(BIN) all: $(BIN) $(PLAYER)
$(LOCALCONFIG): config.def.h $(LOCALCONFIG): config.def.h
cp config.def.h $@ cp config.def.h $@
@ -30,11 +31,13 @@ $(BIN): $(SRC)
install: all install: all
mkdir -p $(DESTDIR)$(PREFIX)/bin mkdir -p $(DESTDIR)$(PREFIX)/bin
cp -f $(BIN) $(DESTDIR)$(PREFIX)/bin cp -f $(BIN) $(DESTDIR)$(PREFIX)/bin
cp -f $(PLAYER) $(DESTDIR)$(PREFIX)/bin
mkdir -p $(DESTDIR)$(MANPREFIX)/man1 mkdir -p $(DESTDIR)$(MANPREFIX)/man1
cp -f $(BIN).1 $(DESTDIR)$(MANPREFIX)/man1 cp -f $(BIN).1 $(DESTDIR)$(MANPREFIX)/man1
uninstall: uninstall:
rm -f $(DESTDIR)$(PREFIX)/bin/$(BIN) rm -f $(DESTDIR)$(PREFIX)/bin/$(BIN)
rm -f $(DESTDIR)$(PREFIX)/bin/$(PLAYER)
rm -f $(DESTDIR)$(MANPREFIX)/man1/$(BIN).1 rm -f $(DESTDIR)$(MANPREFIX)/man1/$(BIN).1
dist: dist:

View file

@ -6,17 +6,18 @@ MANPREFIX = $(PREFIX)/share/man
CFLAGS += -O2 -Wall -Wextra -Wno-unused-parameter CFLAGS += -O2 -Wall -Wextra -Wno-unused-parameter
LDLIBS = -lreadline LDLIBS = -lreadline
ifeq ($(shell uname), Darwin) ifeq ($(shell uname), Darwin)
LDLIBS += -lncurses LDLIBS += -lncurses
else else
LDLIBS += -lncursesw LDLIBS += -lncursesw
endif endif
DISTFILES = nnn.c config.def.h nnn.1 Makefile README.md LICENSE DISTFILES = nlay nnn.c config.def.h nnn.1 Makefile README.md LICENSE
LOCALCONFIG = config.h LOCALCONFIG = config.h
SRC = nnn.c SRC = nnn.c
BIN = nnn BIN = nnn
PLAYER = nlay
all: $(BIN) all: $(BIN) $(PLAYER)
$(LOCALCONFIG): config.def.h $(LOCALCONFIG): config.def.h
cp config.def.h $@ cp config.def.h $@
@ -30,11 +31,13 @@ $(BIN): $(SRC)
install: all install: all
mkdir -p $(DESTDIR)$(PREFIX)/bin mkdir -p $(DESTDIR)$(PREFIX)/bin
cp -f $(BIN) $(DESTDIR)$(PREFIX)/bin cp -f $(BIN) $(DESTDIR)$(PREFIX)/bin
cp -f $(PLAYER) $(DESTDIR)$(PREFIX)/bin
mkdir -p $(DESTDIR)$(MANPREFIX)/man1 mkdir -p $(DESTDIR)$(MANPREFIX)/man1
cp -f $(BIN).1 $(DESTDIR)$(MANPREFIX)/man1 cp -f $(BIN).1 $(DESTDIR)$(MANPREFIX)/man1
uninstall: uninstall:
rm -f $(DESTDIR)$(PREFIX)/bin/$(BIN) rm -f $(DESTDIR)$(PREFIX)/bin/$(BIN)
rm -f $(DESTDIR)$(PREFIX)/bin/$(PLAYER)
rm -f $(DESTDIR)$(MANPREFIX)/man1/$(BIN).1 rm -f $(DESTDIR)$(MANPREFIX)/man1/$(BIN).1
dist: dist:

View file

@ -20,6 +20,7 @@ Noice is Not Noice, a noicer fork...
- [Introduction](#introduction) - [Introduction](#introduction)
- [Features](#features) - [Features](#features)
- [Performance](#performance) - [Performance](#performance)
- [nlay](#nlay)
- [Installation](#installation) - [Installation](#installation)
- [Usage](#usage) - [Usage](#usage)
- [Cmdline options](#cmdline-options) - [Cmdline options](#cmdline-options)
@ -55,6 +56,7 @@ Have fun with it! PRs are welcome. Check out [#1](https://github.com/jarun/nnn/i
- Super-easy navigation with roll-over at edges - 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 you normally do!)
- Desktop opener integration to handle mime types - Desktop opener integration to handle mime types
- Customizable bash script nlay to handle known file types
- Disk usage analyzer mode - Disk usage analyzer mode
- Basic and detail views - Basic and detail views
- Show stat and file information - Show stat and file information
@ -88,6 +90,10 @@ nnn vs. mc vs. ranger memory usage while viewing a directory with 10,178 files,
28863 vaio 20 0 19876 6436 2620 S 0.0 0.1 0:00.19 nnn -d 28863 vaio 20 0 19876 6436 2620 S 0.0 0.1 0:00.19 nnn -d
``` ```
### nlay
nnn comes with an easily customizable bash shell script to handle files - nlay. To know more about it, visit [nlay on wiki](https://github.com/jarun/nnn/wiki/all-about-nlay).
### Installation ### Installation
nnn needs libreadline, libncursesw (on Linux or ncurses on OS X) and standard libc. nnn needs libreadline, libncursesw (on Linux or ncurses on OS X) and standard libc.
@ -180,22 +186,29 @@ The following abbreviations are used in the detail view:
| `c` | Character Device | | `c` | Character Device |
#### File handling #### File handling
nnn is designed to play files using multiple strategies (in order of decreasing priority):
- Set `NNN_OPENER` to let a desktop opener handle it all. E.g.: - Set `NNN_OPENER` to let a desktop opener handle it all. E.g.:
export NNN_OPENER=xdg-open export NNN_OPENER=xdg-open
export NNN_OPENER="gio open" export NNN_OPENER="gio open"
export NNN_OPENER=gvfs-open export NNN_OPENER=gvfs-open
- Selective file associations (ignored if `NNN_OPENER` is set): - If nnn recognizes the file extension, it invokes nlay (which invokes the players). Default players:
- vi - plain text files (determined using file) - mpv - audio and video
- mpv - common audio and video mimes - viewnior - image
- [zathura](https://pwmt.org/projects/zathura/) - pdf files - [zathura](https://pwmt.org/projects/zathura/) - pdf
- to customize further see [how to change file associations](#change-file-associations) - vim - plain text
- Set `NNN_FALLBACK_OPENER` as the fallback opener: - to add, remove recognized extensions in nnn, see [how to change file associations](#change-file-associations)
- if the executable in static file association is missing - If a file without any extension is a plain text file, it is opened in vi
- if a file type was not handled in static file association - Set `NNN_FALLBACK_OPENER` as the fallback opener. E.g.:
export NNN_FALLBACK_OPENER=xdg-open
export NNN_FALLBACK_OPENER="gio open"
export NNN_FALLBACK_OPENER=gvfs-open
- To enable the desktop file manager key, set `NNN_DE_FILE_MANAGER`. E.g.: - To enable the desktop file manager key, set `NNN_DE_FILE_MANAGER`. E.g.:
export NNN_DE_FILE_MANAGER=thunar export NNN_DE_FILE_MANAGER=thunar
export NNN_DE_FILE_MANAGER=nautilus
- [mediainfo](https://mediaarea.net/en/MediaInfo) is required to view media information - [mediainfo](https://mediaarea.net/en/MediaInfo) is required to view media information
#### Help #### Help
@ -246,7 +259,11 @@ Start nnn and use `^K` to copy the absolute path (from `/`) of the file under th
#### Change file associations #### Change file associations
If you want to set custom applications for certain mime types, or change the ones set already (e.g. vi, mpv, zathura), modify the `assocs` structure in [config.def.h](https://github.com/jarun/nnn/blob/master/config.def.h) (it's easy). Then re-compile and install. If `NNN_OPENER` is not set, nnn tries to recognize a file by the file extension and invokes nlay. To change the extensions recognized by nnn, modify the `assocs` structure in [config.def.h](https://github.com/jarun/nnn/blob/master/config.def.h) (it's easy). Then re-compile and install.
If you want to add a file extension mainline, please raise a bug. Without it nnn will not invoke nlay.
nlay has provisions (disabled by default) to handle a specific file extension too. However, the extension should be recognized by nnn first.
### Why fork? ### Why fork?

View file

@ -11,18 +11,15 @@ static int showhidden = 0; /* Set to 1 to show hidden files by default */
static int showdetail = 0; /* Set to show additional file info */ static int showdetail = 0; /* Set to show additional file info */
static char *idlecmd = "rain"; /* The screensaver program */ static char *idlecmd = "rain"; /* The screensaver program */
struct assoc assocs[] = { static struct assoc assocs[] = {
{ "\\.(c|cpp|h|txt|log|sh)$", "vi" }, { "\\.(c|cpp|h|log|md|py|sh|txt)$", "text" },
{ "\\.(avi|mp4|mkv|3gp|mov)$", "mpv" }, { "\\.(3g2|3gp|asf|avi|divx|flv|m2v|m4v|mkv|mov|mp4|mp4v|mpeg|mpg|ogv|qt|rm|rmvb|vob|webm|wmv)$", "video" },
{ "\\.(wma|mp3|ogg|flac|m4a)$", "mpv" }, { "\\.(aac|ac3|amr|flac|m4a|m4b|m4p|mp3|mp4a|ogg|opus|ra|wav|wma)$", "audio" },
{ "\\.(png|jpg|gif)$", "viewnior" }, { "\\.(bmp|gif|jpeg|jpg|pbm|pgm|png|svg|tiff|webp)$", "image" },
//{ "\\.(html|svg)$", "firefox" }, { "\\.pdf$", "pdf" },
{ "\\.pdf$", "zathura" },
//{ "\\.sh$", "sh" },
//{ ".", "less" },
}; };
struct key bindings[] = { static struct key bindings[] = {
/* Quit */ /* Quit */
{ 'q', SEL_QUIT, "", "" }, { 'q', SEL_QUIT, "", "" },
{ 'Q', SEL_CDQUIT, "", "" }, { 'Q', SEL_CDQUIT, "", "" },

119
nlay Executable file
View file

@ -0,0 +1,119 @@
#!/bin/bash
# #############################################################################
# nlay: a customizable script to play files in different apps by file type
#
# usage: nlay file type
#
# MUST READ:
#
# 1. Feel free to change the default apps to your favourite ones.
# If you change the app for a group you may also need to modify the bg
# setting. If bg is set the app is detached and started in the background in
# silent mode.
#
# The bg setting depends on personal preference and type of app, e.g.,
# I would start vim (CLI) in the foreground but Sublime Text (GUI) in the
# background.
#
# Check (and TOGGLE as you wish) the default bg settings.
#
# 2. Detached apps are not killed when nnn exits. Use kill(1) or killall(1) to
# to stop console based background apps.
#
# 3. Assuming you don't to play multiple audio/video files simultaneously,
# nlay kills any running instances of the audio/video player in bg mode.
#
# 4. Keep a personal backup (on GitHub Gist maybe?) of this file if you modify
# it. nlay is OVERWRITTEN during nnn upgrade.
#
# Author: Arun Prakash Jana
# Email: engineerarun@gmail.com
# #############################################################################
# Enable the lines below to handle file by extension
# This is provided for using a custom player for specific files
# $ext holds the extension
<<ENABLE_FILE_TYPE_HANDLING
fname=$(basename "$1")
if [[ $fname != *"."* ]]; then
exit 1
fi
ext="${fname##*.}"
if [ -z "$ext" ]; then
exit 1
fi
# bash 4.0 way to switch to lowercase
ext="${ext,,}"
# handle this extension and exit
ENABLE_FILE_TYPE_HANDLING
#------------------ AUDIO -------------------
if [ "$2" == "audio" ]; then
app=mpv
# To start mpv in a window enable audio_opts
#audio_opts="--no-terminal --force-window"
#bg=">/dev/null 2>&1 &"
if [ -n "$bg" ]; then
killall -9 $app >/dev/null 2>&1
fi
eval $app $audio_opts "\"$1\"" $bg
exit 0
fi
#------------------ VIDEO -------------------
if [ "$2" == "video" ]; then
app=mpv
# To start mpv in a window enable video_opts
#video_opts="--no-terminal --force-window"
#bg=">/dev/null 2>&1 &"
if [ -n "$bg" ]; then
killall -9 $app >/dev/null 2>&1
fi
eval $app $video_opts "\"$1\"" $bg
exit 0
fi
#------------------ IMAGE -------------------
if [ "$2" == "image" ]; then
app=viewnior
#image_opts=
bg=">/dev/null 2>&1 &"
eval $app $image_opts "\"$1\"" $bg
exit 0
fi
#------------------- PDF --------------------
if [ "$2" == "pdf" ]; then
app=zathura
#pdf_opts=
bg=">/dev/null 2>&1 &"
eval $app $pdf_opts "\"$1\"" $bg
exit 0
fi
#---------------- PLAINTEXT -----------------
if [ "$2" == "text" ]; then
app=vim
#txt_opts=
#bg=">/dev/null 2>&1 &"
eval $app $txt_opts "\"$1\"" $bg
exit 0
fi

33
nnn.1
View file

@ -106,9 +106,22 @@ supports the following options:
show program help and exit show program help and exit
.Sh CONFIGURATION .Sh CONFIGURATION
.Nm .Nm
invokes
.Pa nlay
to play a file if it recognizes a file by extension.
.Pa nlay
is a
highly customizable bash shell script which invokes a player depending on the
type of file. Read more on
.Pa nlay
at:
.br
.Em https://github.com/jarun/nnn/wiki/all-about-nlay
.Pp
.Nm
is configured by modifying is configured by modifying
.Pa config.h .Pa config.def.h
and recompiling the code. and recompiling the code. config.h is generated as a backup of config.def.h.
.Pp .Pp
See the environment and examples sections below for more options and information. See the environment and examples sections below for more options and information.
.Pp .Pp
@ -154,22 +167,6 @@ type. Custom associations are listed in the EXAMPLES section below.
echo -n $1 | xsel --clipboard --input echo -n $1 | xsel --clipboard --input
------------------------------------- -------------------------------------
.Ed .Ed
.Sh EXAMPLES
The following example shows one possible configuration for
file associations which is also the default if environment
variable NNN_OPENER is not set:
.Bd -literal
-----------------------------------------------
struct assoc assocs[] = {
{ "\\.(c|cpp|h|txt|log|sh)$", "vi" },
{ "\\.(wma|mp3|ogg|flac)$", "mpv" },
{ "\\.pdf$", "zathura" },
};
-----------------------------------------------
Plain text files are opened with vi.
.br
Any other file types are opened with the 'xdg-open' command.
.Ed
.Sh KNOWN ISSUES .Sh KNOWN ISSUES
If you are using urxvt you might have to set backspacekey to DEC. If you are using urxvt you might have to set backspacekey to DEC.
.Sh AUTHORS .Sh AUTHORS

95
nnn.c
View file

@ -68,7 +68,7 @@ xprintf(int fd, const char *fmt, ...)
struct assoc { struct assoc {
char *regex; /* Regex to match on filename */ char *regex; /* Regex to match on filename */
char *bin; /* Program */ char *mime; /* File type */
}; };
/* Supported actions */ /* Supported actions */
@ -134,7 +134,7 @@ static struct entry *dents;
static int ndents, cur, total_dents; static int ndents, cur, total_dents;
static int idle; static int idle;
static char *opener; static char *opener;
static char *fallback_opener; static char *fb_opener;
static char *copier; static char *copier;
static char *desktop_manager; static char *desktop_manager;
static off_t blk_size; static off_t blk_size;
@ -384,23 +384,20 @@ strstrip(char *s)
} }
static char * static char *
openwith(char *file) getmime(char *file)
{ {
regex_t regex; regex_t regex;
char *bin = NULL;
unsigned int i; unsigned int i;
static unsigned int len = LEN(assocs);
for (i = 0; i < LEN(assocs); i++) { for (i = 0; i < len; i++) {
if (regcomp(&regex, assocs[i].regex, if (regcomp(&regex, assocs[i].regex,
REG_NOSUB | REG_EXTENDED | REG_ICASE) != 0) REG_NOSUB | REG_EXTENDED | REG_ICASE) != 0)
continue; continue;
if (regexec(&regex, file, 0, NULL, 0) == 0) { if (regexec(&regex, file, 0, NULL, 0) == 0)
bin = assocs[i].bin; return assocs[i].mime;
break;
}
} }
DPRINTF_S(bin); return NULL;
return bin;
} }
static int static int
@ -540,6 +537,7 @@ nextsel(char **run, char **env)
{ {
int c; int c;
unsigned int i; unsigned int i;
static unsigned int len = LEN(bindings);
c = getch(); c = getch();
if (c == -1) if (c == -1)
@ -547,7 +545,7 @@ nextsel(char **run, char **env)
else else
idle = 0; idle = 0;
for (i = 0; i < LEN(bindings); i++) for (i = 0; i < len; i++)
if (c == bindings[i].sym) { if (c == bindings[i].sym) {
*run = bindings[i].run; *run = bindings[i].run;
*env = bindings[i].env; *env = bindings[i].env;
@ -1248,7 +1246,7 @@ browse(char *ipath, char *ifilter)
static char path[PATH_MAX], oldpath[PATH_MAX], newpath[PATH_MAX]; static char path[PATH_MAX], oldpath[PATH_MAX], newpath[PATH_MAX];
static char lastdir[PATH_MAX]; static char lastdir[PATH_MAX];
static char fltr[LINE_MAX]; static char fltr[LINE_MAX];
char *bin, *dir, *tmp, *run, *env; char *mime, *dir, *tmp, *run, *env;
struct stat sb; struct stat sb;
regex_t re; regex_t re;
int r, fd; int r, fd;
@ -1352,9 +1350,8 @@ nochange:
case S_IFREG: case S_IFREG:
{ {
static char cmd[MAX_CMD_LEN]; static char cmd[MAX_CMD_LEN];
static char *runvi = "vi";
/* If default mime opener is set, use it */ /* If NNN_OPENER is set, use it */
if (opener) { if (opener) {
snprintf(cmd, MAX_CMD_LEN, snprintf(cmd, MAX_CMD_LEN,
"%s \"%s\" > /dev/null 2>&1", "%s \"%s\" > /dev/null 2>&1",
@ -1363,42 +1360,38 @@ nochange:
continue; continue;
} }
/* Try custom applications */ /* Play with nlay if identified */
bin = openwith(newpath); mime = getmime(dents[cur].name);
if (mime) {
/* If custom app doesn't exist try fallback */ snprintf(cmd, MAX_CMD_LEN, "nlay \"%s\" %s",
snprintf(cmd, MAX_CMD_LEN, "which \"%s\"", bin); newpath, mime);
if (get_output(cmd, MAX_CMD_LEN) == NULL) exitcurses();
bin = NULL; r = system(cmd);
initcurses();
if (bin == NULL) { continue;
/* If a custom handler application is
not set, open plain text files with
vi, then try fallback_opener */
snprintf(cmd, MAX_CMD_LEN,
"file \"%s\"", newpath);
if (get_output(cmd, MAX_CMD_LEN) == NULL)
goto nochange;
if (strstr(cmd, "ASCII text") != NULL)
bin = runvi;
else if (fallback_opener) {
snprintf(cmd, MAX_CMD_LEN,
"%s \"%s\" > \
/dev/null 2>&1",
fallback_opener,
newpath);
r = system(cmd);
continue;
} else {
printmsg("No association");
goto nochange;
}
} }
exitcurses();
spawn(bin, newpath, NULL, 0); /* If nlay doesn't handle it, open plain text
initcurses(); files with vi, then try NNN_FALLBACK_OPENER */
continue; snprintf(cmd, MAX_CMD_LEN,
"file \"%s\"", newpath);
if (get_output(cmd, MAX_CMD_LEN) == NULL)
continue;
if (strstr(cmd, "ASCII text") != NULL) {
exitcurses();
spawn("vi", newpath, NULL, 0);
initcurses();
continue;
} else if (fb_opener) {
snprintf(cmd, MAX_CMD_LEN, "%s \"%s\" > /dev/null 2>&1",
fb_opener, newpath);
r = system(cmd);
continue;
}
printmsg("No association");
goto nochange;
} }
default: default:
printmsg("Unsupported file"); printmsg("Unsupported file");
@ -1788,7 +1781,7 @@ main(int argc, char *argv[])
opener = getenv("NNN_OPENER"); opener = getenv("NNN_OPENER");
/* Get the fallback desktop mime opener, if set */ /* Get the fallback desktop mime opener, if set */
fallback_opener = getenv("NNN_FALLBACK_OPENER"); fb_opener = getenv("NNN_FALLBACK_OPENER");
/* Get the desktop file browser, if set */ /* Get the desktop file browser, if set */
desktop_manager = getenv("NNN_DE_FILE_MANAGER"); desktop_manager = getenv("NNN_DE_FILE_MANAGER");