Implement nopen(1) - noice - small file browser (mirror / fork from 2f30.org) | |
git clone git://git.codemadness.org/noice | |
Log | |
Files | |
Refs | |
README | |
LICENSE | |
--- | |
commit 50592339bf450037972b85777d1c524e35545aa8 | |
parent 47d659c5fc930f0815c2bf5a24b3c2228b13695e | |
Author: sin <[email protected]> | |
Date: Fri, 2 Aug 2019 15:43:07 +0100 | |
Implement nopen(1) | |
Diffstat: | |
M Makefile | 48 ++++++++++++++++++-----------… | |
D config.def.h | 102 -----------------------------… | |
M noice.1 | 24 ++++++++---------------- | |
M noice.c | 113 ++---------------------------… | |
A noiceconf.def.h | 93 +++++++++++++++++++++++++++++… | |
A nopen.1 | 31 +++++++++++++++++++++++++++++… | |
A nopen.c | 95 ++++++++++++++++++++++++++++++ | |
A nopenconf.def.h | 9 +++++++++ | |
A spawn.c | 43 ++++++++++++++++++++++++++++++ | |
M strverscmp.c | 2 ++ | |
M util.h | 21 +++++++++++++-------- | |
11 files changed, 327 insertions(+), 254 deletions(-) | |
--- | |
diff --git a/Makefile b/Makefile | |
@@ -1,45 +1,53 @@ | |
VERSION = 0.8 | |
- | |
PREFIX = /usr/local | |
MANPREFIX = $(PREFIX)/man | |
#CPPFLAGS = -DDEBUG | |
#CFLAGS = -g | |
-LDLIBS = -lcurses | |
-DISTFILES = noice.c strlcat.c strlcpy.c strverscmp.c util.h config.def.h\ | |
- noice.1 Makefile README LICENSE | |
-OBJ = noice.o strlcat.o strlcpy.o strverscmp.o | |
-BIN = noice | |
+NOICELDLIBS = -lcurses | |
+NOPENLDLIBS = | |
+NOICEOBJ = noice.o spawn.o strlcat.o strlcpy.o strverscmp.o | |
+NOPENOBJ = nopen.o spawn.o | |
+BIN = noice nopen | |
+MAN = noice.1 nopen.1 | |
all: $(BIN) | |
-$(BIN): $(OBJ) | |
- $(CC) $(CFLAGS) -o $@ $(OBJ) $(LDFLAGS) $(LDLIBS) | |
+noice: $(NOICEOBJ) | |
+ $(CC) $(CFLAGS) -o $@ $(NOICEOBJ) $(LDFLAGS) $(NOICELDLIBS) | |
+ | |
+nopen: $(NOPENOBJ) | |
+ $(CC) $(CFLAGS) -o $@ $(NOPENOBJ) $(LDFLAGS) $(NOPENLDLIBS) | |
-noice.o: util.h config.h | |
+noice.o: noiceconf.h util.h | |
+nopen.o: nopenconf.h util.h | |
+spawn.o: util.h | |
strlcat.o: util.h | |
strlcpy.o: util.h | |
+strverscmp.o: util.h | |
+ | |
+noiceconf.h: | |
+ cp noiceconf.def.h $@ | |
-config.h: | |
- cp config.def.h $@ | |
+nopenconf.h: | |
+ cp nopenconf.def.h $@ | |
install: all | |
mkdir -p $(DESTDIR)$(PREFIX)/bin | |
cp -f $(BIN) $(DESTDIR)$(PREFIX)/bin | |
mkdir -p $(DESTDIR)$(MANPREFIX)/man1 | |
- cp -f $(BIN).1 $(DESTDIR)$(MANPREFIX)/man1 | |
+ cp -f $(MAN) $(DESTDIR)$(MANPREFIX)/man1 | |
uninstall: | |
- rm -f $(DESTDIR)$(PREFIX)/bin/$(BIN) | |
- rm -f $(DESTDIR)$(MANPREFIX)/man1/$(BIN).1 | |
+ cd $(DESTDIR)$(PREFIX)/bin && rm -f $(BIN) | |
+ cd $(DESTDIR)$(MANPREFIX)/man1 && rm -f $(MAN) | |
-dist: | |
+dist: clean | |
mkdir -p noice-$(VERSION) | |
- cp $(DISTFILES) noice-$(VERSION) | |
- tar -cf noice-$(VERSION).tar noice-$(VERSION) | |
- gzip noice-$(VERSION).tar | |
- rm -rf noice-$(VERSION) | |
+ cp `find . -maxdepth 1 -type f` noice-$(VERSION) | |
+ tar -c noice-$(VERSION) | gzip > noice-$(VERSION).tar.gz | |
clean: | |
- rm -f $(BIN) $(OBJ) noice-$(VERSION).tar.gz | |
+ rm -f $(BIN) $(NOICEOBJ) $(NOPENOBJ) noice-$(VERSION).tar.gz | |
+ rm -rf noice-$(VERSION) | |
diff --git a/config.def.h b/config.def.h | |
@@ -1,102 +0,0 @@ | |
-/* See LICENSE file for copyright and license details. */ | |
-#define CWD "cwd: " | |
-#define CURSR " > " | |
-#define EMPTY " " | |
- | |
-int dirorder = 0; /* Set to 1 to sort by directory first */ | |
-int mtimeorder = 0; /* Set to 1 to sort by time modified */ | |
-int icaseorder = 0; /* Set to 1 to sort by ignoring case */ | |
-int versorder = 0; /* Set to 1 to sort by version number */ | |
-int idletimeout = 0; /* Screensaver timeout in seconds, 0 to disable */ | |
-int showhidden = 0; /* Set to 1 to show hidden files by default */ | |
-int usecolor = 0; /* Set to 1 to enable color attributes */ | |
-char *idlecmd = "rain"; /* The screensaver program */ | |
- | |
-/* See curs_attr(3) for valid video attributes */ | |
-#define CURSR_ATTR A_NORMAL | |
-#define DIR_ATTR A_NORMAL | COLOR_PAIR(4) | |
-#define LINK_ATTR A_NORMAL | COLOR_PAIR(6) | |
-#define SOCK_ATTR A_NORMAL | COLOR_PAIR(1) | |
-#define FIFO_ATTR A_NORMAL | COLOR_PAIR(5) | |
-#define EXEC_ATTR A_NORMAL | COLOR_PAIR(2) | |
- | |
-/* Colors to use with COLOR_PAIR(n) as attributes */ | |
-struct cpair pairs[] = { | |
- { .fg = 0, .bg = 0 }, | |
- /* pairs start at 1 */ | |
- { COLOR_RED, -1 }, | |
- { COLOR_GREEN, -1 }, | |
- { COLOR_YELLOW, -1 }, | |
- { COLOR_BLUE, -1 }, | |
- { COLOR_MAGENTA, -1 }, | |
- { COLOR_CYAN, -1 }, | |
-}; | |
- | |
-struct assoc assocs[] = { | |
- { .regex = "\\.(avi|mp4|mkv|mp3|ogg|flac|mov)$", .file = "mpv", .argv … | |
- { .regex = "\\.(png|jpg|gif)$", .file = "sxiv", .argv = { "sxiv", "{}"… | |
- { .regex = "\\.(html|svg)$", .file = "firefox", .argv = { "firefox", "… | |
- { .regex = "\\.pdf$", .file = "mupdf", .argv = { "mupdf", "{}", NULL} … | |
- { .regex = "\\.sh$", .file = "sh", .argv = { "sh", "{}", NULL} }, | |
- { .regex = ".", .file = "less", .argv = { "less", "{}", NULL } }, | |
-}; | |
- | |
-struct key bindings[] = { | |
- /* Quit */ | |
- { 'q', SEL_QUIT }, | |
- /* Back */ | |
- { KEY_BACKSPACE, SEL_BACK }, | |
- { KEY_LEFT, SEL_BACK }, | |
- { 'h', SEL_BACK }, | |
- { CONTROL('H'), SEL_BACK }, | |
- /* Inside */ | |
- { KEY_ENTER, SEL_GOIN }, | |
- { '\r', SEL_GOIN }, | |
- { KEY_RIGHT, SEL_GOIN }, | |
- { 'l', SEL_GOIN }, | |
- /* Filter */ | |
- { '/', SEL_FLTR }, | |
- { '&', SEL_FLTR }, | |
- /* Next */ | |
- { 'j', SEL_NEXT }, | |
- { KEY_DOWN, SEL_NEXT }, | |
- { CONTROL('N'), SEL_NEXT }, | |
- /* Previous */ | |
- { 'k', SEL_PREV }, | |
- { KEY_UP, SEL_PREV }, | |
- { CONTROL('P'), SEL_PREV }, | |
- /* Page down */ | |
- { KEY_NPAGE, SEL_PGDN }, | |
- { CONTROL('D'), SEL_PGDN }, | |
- /* Page up */ | |
- { KEY_PPAGE, SEL_PGUP }, | |
- { CONTROL('U'), SEL_PGUP }, | |
- /* Home */ | |
- { KEY_HOME, SEL_HOME }, | |
- { META('<'), SEL_HOME }, | |
- { '^', SEL_HOME }, | |
- /* End */ | |
- { KEY_END, SEL_END }, | |
- { META('>'), SEL_END }, | |
- { '$', SEL_END }, | |
- /* Change dir */ | |
- { 'c', SEL_CD }, | |
- { '~', SEL_CDHOME }, | |
- /* Toggle hide .dot files */ | |
- { '.', SEL_TOGGLEDOT }, | |
- /* Toggle sort by directory first */ | |
- { 'd', SEL_DSORT }, | |
- /* Toggle sort by time */ | |
- { 't', SEL_MTIME }, | |
- /* Toggle case sensitivity */ | |
- { 'i', SEL_ICASE }, | |
- /* Toggle sort by version number */ | |
- { 'v', SEL_VERS }, | |
- { CONTROL('L'), SEL_REDRAW }, | |
- /* Run command */ | |
- { 'z', SEL_RUN, "top" }, | |
- { '!', SEL_RUN, "sh", "SHELL" }, | |
- /* Run command with argument */ | |
- { 'e', SEL_RUNARG, "vi", "EDITOR" }, | |
- { 'p', SEL_RUNARG, "less", "PAGER" }, | |
-}; | |
diff --git a/noice.1 b/noice.1 | |
@@ -1,4 +1,4 @@ | |
-.Dd March 31, 2019 | |
+.Dd August 2, 2019 | |
.Dt NOICE 1 | |
.Os | |
.Sh NAME | |
@@ -84,23 +84,13 @@ directory you came out of. | |
.Sh CONFIGURATION | |
.Nm | |
is configured by modifying | |
-.Pa config.h | |
+.Pa noiceconf.h | |
and recompiling the code. | |
.Pp | |
-The file associations are specified by regexes | |
-matching on the currently selected filename. | |
-If a match is found the associated program is executed | |
-with the filename passed in as the argument. | |
-If no match is found the program | |
-.Xr less 1 | |
-is invoked. | |
-This is useful for editing text files as one can use the | |
-.Ic v | |
-command in | |
-.Xr less 1 | |
-to edit the file using the | |
-.Ev EDITOR | |
-environment variable. | |
+.Nm | |
+invokes | |
+.Xr nopen 1 | |
+to open a file in the user's preferred application. | |
.Sh FILTERS | |
Filters allow you to use regexes to display only the matched | |
entries in the current directory view. | |
@@ -131,6 +121,8 @@ commands respectively. | |
If you are using | |
.Xr urxvt 1 | |
you might have to set backspace key to DEC. | |
+.Sh SEE ALSO | |
+.Xr nopen 1 | |
.Sh AUTHORS | |
.An Lazaros Koromilas Aq Mt [email protected] , | |
.An Dimitris Papastamos Aq Mt [email protected] . | |
diff --git a/noice.c b/noice.c | |
@@ -1,7 +1,6 @@ | |
/* See LICENSE file for copyright and license details. */ | |
#include <sys/stat.h> | |
#include <sys/types.h> | |
-#include <sys/wait.h> | |
#include <curses.h> | |
#include <dirent.h> | |
@@ -20,21 +19,12 @@ | |
#include "util.h" | |
-#define NR_ARGS 32 | |
-#define LEN(x) (sizeof(x) / sizeof(*(x))) | |
#undef MIN | |
#define MIN(x, y) ((x) < (y) ? (x) : (y)) | |
#define ISODD(x) ((x) & 1) | |
#define CONTROL(c) ((c) ^ 0x40) | |
#define META(c) ((c) ^ 0x80) | |
-struct assoc { | |
- char *regex; /* Regex to match on filename */ | |
- char *file; | |
- char *argv[NR_ARGS]; | |
- regex_t regcomp; | |
-}; | |
- | |
struct cpair { | |
int fg; | |
int bg; | |
@@ -71,7 +61,7 @@ struct key { | |
char *env; /* Environment variable to run */ | |
}; | |
-#include "config.h" | |
+#include "noiceconf.h" | |
struct entry { | |
char name[PATH_MAX]; | |
@@ -167,59 +157,6 @@ xdirname(const char *path) | |
return out; | |
} | |
-void | |
-spawnvp(char *dir, char *file, char *argv[]) | |
-{ | |
- pid_t pid; | |
- int status; | |
- | |
- pid = fork(); | |
- if (pid == 0) { | |
- if (dir != NULL) | |
- chdir(dir); | |
- execvp(file, argv); | |
- _exit(1); | |
- } else { | |
- /* Ignore interruptions */ | |
- while (waitpid(pid, &status, 0) == -1) | |
- DPRINTF_D(status); | |
- DPRINTF_D(pid); | |
- } | |
-} | |
- | |
-void | |
-spawnlp(char *dir, char *file, char *argv0, ...) | |
-{ | |
- char *argv[NR_ARGS]; | |
- va_list ap; | |
- int argc; | |
- | |
- va_start(ap, argv0); | |
- argv[0] = argv0; | |
- for (argc = 1; argv[argc] = va_arg(ap, char *); argc++) | |
- ; | |
- argv[argc] = NULL; | |
- va_end(ap); | |
- spawnvp(dir, file, argv); | |
-} | |
- | |
-void | |
-spawnassoc(struct assoc *assoc, char *arg) | |
-{ | |
- char *argv[NR_ARGS]; | |
- int i; | |
- | |
- for (i = 0; assoc->argv[i]; i++) { | |
- if (strcmp(assoc->argv[i], "{}") == 0) { | |
- argv[i] = arg; | |
- continue; | |
- } | |
- argv[i] = assoc->argv[i]; | |
- } | |
- argv[i] = NULL; | |
- spawnvp(NULL, assoc->file, argv); | |
-} | |
- | |
char * | |
xgetenv(char *name, char *fallback) | |
{ | |
@@ -231,21 +168,6 @@ xgetenv(char *name, char *fallback) | |
return value && value[0] ? value : fallback; | |
} | |
-struct assoc * | |
-openwith(char *file) | |
-{ | |
- int i; | |
- | |
- for (i = 0; i < LEN(assocs); i++) { | |
- if (regexec(&assocs[i].regcomp, file, 0, NULL, 0) == 0) { | |
- DPRINTF_S(assocs[i].argv[0]); | |
- return &assocs[i]; | |
- } | |
- } | |
- | |
- return NULL; | |
-} | |
- | |
int | |
setfilter(regex_t *regex, char *filter) | |
{ | |
@@ -671,7 +593,6 @@ browse(char *ipath, char *ifilter) | |
char path[PATH_MAX], oldpath[PATH_MAX], newpath[PATH_MAX]; | |
char fltr[LINE_MAX]; | |
char *dir, *tmp, *run, *env; | |
- struct assoc *assoc; | |
struct stat sb; | |
regex_t re; | |
int r, fd; | |
@@ -744,13 +665,8 @@ nochange: | |
strlcpy(fltr, ifilter, sizeof(fltr)); | |
goto begin; | |
case S_IFREG: | |
- assoc = openwith(newpath); | |
- if (assoc == NULL) { | |
- printmsg("No association"); | |
- goto nochange; | |
- } | |
exitcurses(); | |
- spawnassoc(assoc, newpath); | |
+ spawnlp(path, "nopen", "nopen", newpath, (void… | |
initcurses(); | |
continue; | |
default: | |
@@ -869,7 +785,7 @@ nochange: | |
mkpath(path, dents[cur].name, oldpath, sizeof(… | |
run = xgetenv(env, run); | |
exitcurses(); | |
- spawnlp(path, run, run, NULL); | |
+ spawnlp(path, run, run, (void *)0); | |
initcurses(); | |
goto begin; | |
case SEL_RUNARG: | |
@@ -878,7 +794,7 @@ nochange: | |
mkpath(path, dents[cur].name, oldpath, sizeof(… | |
run = xgetenv(env, run); | |
exitcurses(); | |
- spawnlp(path, run, run, dents[cur].name, NULL); | |
+ spawnlp(path, run, run, dents[cur].name, (void *)0); | |
initcurses(); | |
goto begin; | |
} | |
@@ -886,31 +802,13 @@ nochange: | |
if (idletimeout != 0 && idle == idletimeout) { | |
idle = 0; | |
exitcurses(); | |
- spawnlp(NULL, idlecmd, idlecmd, NULL); | |
+ spawnlp(NULL, idlecmd, idlecmd, (void *)0); | |
initcurses(); | |
} | |
} | |
} | |
void | |
-initassocs(void) | |
-{ | |
- char errbuf[256]; | |
- int i, r; | |
- | |
- for (i = 0; i < LEN(assocs); i++) { | |
- r = regcomp(&assocs[i].regcomp, assocs[i].regex, | |
- REG_NOSUB | REG_EXTENDED | REG_ICASE); | |
- if (r != 0) { | |
- regerror(r, &assocs[i].regcomp, errbuf, sizeof(errbuf)… | |
- fprintf(stderr, "invalid regex assocs[%d]: %s: %s\n", | |
- i, assocs[i].regex, errbuf); | |
- exit(1); | |
- } | |
- } | |
-} | |
- | |
-void | |
usage(char *argv0) | |
{ | |
fprintf(stderr, "usage: %s [dir]\n", argv0); | |
@@ -954,7 +852,6 @@ main(int argc, char *argv[]) | |
/* Set locale before curses setup */ | |
setlocale(LC_ALL, ""); | |
- initassocs(); | |
initcurses(); | |
browse(ipath, ifilter); | |
exitcurses(); | |
diff --git a/noiceconf.def.h b/noiceconf.def.h | |
@@ -0,0 +1,93 @@ | |
+/* See LICENSE file for copyright and license details. */ | |
+#define CWD "cwd: " | |
+#define CURSR " > " | |
+#define EMPTY " " | |
+ | |
+int dirorder = 0; /* Set to 1 to sort by directory first */ | |
+int mtimeorder = 0; /* Set to 1 to sort by time modified */ | |
+int icaseorder = 0; /* Set to 1 to sort by ignoring case */ | |
+int versorder = 0; /* Set to 1 to sort by version number */ | |
+int idletimeout = 0; /* Screensaver timeout in seconds, 0 to disable */ | |
+int showhidden = 0; /* Set to 1 to show hidden files by default */ | |
+int usecolor = 0; /* Set to 1 to enable color attributes */ | |
+char *idlecmd = "rain"; /* The screensaver program */ | |
+ | |
+/* See curs_attr(3) for valid video attributes */ | |
+#define CURSR_ATTR A_NORMAL | |
+#define DIR_ATTR A_NORMAL | COLOR_PAIR(4) | |
+#define LINK_ATTR A_NORMAL | COLOR_PAIR(6) | |
+#define SOCK_ATTR A_NORMAL | COLOR_PAIR(1) | |
+#define FIFO_ATTR A_NORMAL | COLOR_PAIR(5) | |
+#define EXEC_ATTR A_NORMAL | COLOR_PAIR(2) | |
+ | |
+/* Colors to use with COLOR_PAIR(n) as attributes */ | |
+struct cpair pairs[] = { | |
+ { .fg = 0, .bg = 0 }, | |
+ /* pairs start at 1 */ | |
+ { COLOR_RED, -1 }, | |
+ { COLOR_GREEN, -1 }, | |
+ { COLOR_YELLOW, -1 }, | |
+ { COLOR_BLUE, -1 }, | |
+ { COLOR_MAGENTA, -1 }, | |
+ { COLOR_CYAN, -1 }, | |
+}; | |
+ | |
+struct key bindings[] = { | |
+ /* Quit */ | |
+ { 'q', SEL_QUIT }, | |
+ /* Back */ | |
+ { KEY_BACKSPACE, SEL_BACK }, | |
+ { KEY_LEFT, SEL_BACK }, | |
+ { 'h', SEL_BACK }, | |
+ { CONTROL('H'), SEL_BACK }, | |
+ /* Inside */ | |
+ { KEY_ENTER, SEL_GOIN }, | |
+ { '\r', SEL_GOIN }, | |
+ { KEY_RIGHT, SEL_GOIN }, | |
+ { 'l', SEL_GOIN }, | |
+ /* Filter */ | |
+ { '/', SEL_FLTR }, | |
+ { '&', SEL_FLTR }, | |
+ /* Next */ | |
+ { 'j', SEL_NEXT }, | |
+ { KEY_DOWN, SEL_NEXT }, | |
+ { CONTROL('N'), SEL_NEXT }, | |
+ /* Previous */ | |
+ { 'k', SEL_PREV }, | |
+ { KEY_UP, SEL_PREV }, | |
+ { CONTROL('P'), SEL_PREV }, | |
+ /* Page down */ | |
+ { KEY_NPAGE, SEL_PGDN }, | |
+ { CONTROL('D'), SEL_PGDN }, | |
+ /* Page up */ | |
+ { KEY_PPAGE, SEL_PGUP }, | |
+ { CONTROL('U'), SEL_PGUP }, | |
+ /* Home */ | |
+ { KEY_HOME, SEL_HOME }, | |
+ { META('<'), SEL_HOME }, | |
+ { '^', SEL_HOME }, | |
+ /* End */ | |
+ { KEY_END, SEL_END }, | |
+ { META('>'), SEL_END }, | |
+ { '$', SEL_END }, | |
+ /* Change dir */ | |
+ { 'c', SEL_CD }, | |
+ { '~', SEL_CDHOME }, | |
+ /* Toggle hide .dot files */ | |
+ { '.', SEL_TOGGLEDOT }, | |
+ /* Toggle sort by directory first */ | |
+ { 'd', SEL_DSORT }, | |
+ /* Toggle sort by time */ | |
+ { 't', SEL_MTIME }, | |
+ /* Toggle case sensitivity */ | |
+ { 'i', SEL_ICASE }, | |
+ /* Toggle sort by version number */ | |
+ { 'v', SEL_VERS }, | |
+ { CONTROL('L'), SEL_REDRAW }, | |
+ /* Run command */ | |
+ { 'z', SEL_RUN, "top" }, | |
+ { '!', SEL_RUN, "sh", "SHELL" }, | |
+ /* Run command with argument */ | |
+ { 'e', SEL_RUNARG, "vi", "EDITOR" }, | |
+ { 'p', SEL_RUNARG, "less", "PAGER" }, | |
+}; | |
diff --git a/nopen.1 b/nopen.1 | |
@@ -0,0 +1,31 @@ | |
+.Dd August 2, 2019 | |
+.Dt NOPEN 1 | |
+.Os | |
+.Sh NAME | |
+.Nm nopen | |
+.Nd opens a file in the user's preferred application | |
+.Sh SYNOPSIS | |
+.Nm | |
+.Ar file... | |
+.Sh DESCRIPTION | |
+.Nm | |
+opens the files provided as arguments with the user's proferred | |
+application. | |
+.Pp | |
+The file associations are specified by regexes | |
+matching on the currently selected filename. | |
+If a match is found the associated program is executed | |
+with the filename passed in as the argument. | |
+If no match is found the program | |
+.Xr less 1 | |
+is invoked. | |
+.Sh CONFIGURATION | |
+.Nm | |
+is configured by modifying | |
+.Pa nopenconf.h | |
+and recompiling the code. | |
+.Sh SEE ALSO | |
+.Xr noice 1 | |
+.Sh AUTHORS | |
+.An Lazaros Koromilas Aq Mt [email protected] , | |
+.An Dimitris Papastamos Aq Mt [email protected] . | |
diff --git a/nopen.c b/nopen.c | |
@@ -0,0 +1,95 @@ | |
+/* See LICENSE file for copyright and license details. */ | |
+#include <sys/types.h> | |
+#include <sys/wait.h> | |
+ | |
+#include <err.h> | |
+#include <regex.h> | |
+#include <stdio.h> | |
+#include <stdlib.h> | |
+#include <string.h> | |
+#include <unistd.h> | |
+ | |
+#include "util.h" | |
+ | |
+struct assoc { | |
+ char *regex; /* Regex to match on filename */ | |
+ char *file; | |
+ char *argv[NR_ARGS]; | |
+ regex_t regcomp; | |
+}; | |
+ | |
+#include "nopenconf.h" | |
+ | |
+void | |
+spawnassoc(struct assoc *assoc, char *arg) | |
+{ | |
+ char *argv[NR_ARGS]; | |
+ int i; | |
+ | |
+ for (i = 0; assoc->argv[i]; i++) { | |
+ if (strcmp(assoc->argv[i], "{}") == 0) { | |
+ argv[i] = arg; | |
+ continue; | |
+ } | |
+ argv[i] = assoc->argv[i]; | |
+ } | |
+ argv[i] = NULL; | |
+ spawnvp(NULL, assoc->file, argv); | |
+} | |
+ | |
+struct assoc * | |
+openwith(char *file) | |
+{ | |
+ int i; | |
+ | |
+ for (i = 0; i < LEN(assocs); i++) { | |
+ if (regexec(&assocs[i].regcomp, file, 0, NULL, 0) == 0) | |
+ return &assocs[i]; | |
+ } | |
+ | |
+ return NULL; | |
+} | |
+ | |
+void | |
+initassocs(void) | |
+{ | |
+ char errbuf[256]; | |
+ int i, r; | |
+ | |
+ for (i = 0; i < LEN(assocs); i++) { | |
+ r = regcomp(&assocs[i].regcomp, assocs[i].regex, | |
+ REG_NOSUB | REG_EXTENDED | REG_ICASE); | |
+ if (r != 0) { | |
+ regerror(r, &assocs[i].regcomp, errbuf, sizeof(errbuf)… | |
+ fprintf(stderr, "invalid regex assocs[%d]: %s: %s\n", | |
+ i, assocs[i].regex, errbuf); | |
+ exit(1); | |
+ } | |
+ } | |
+} | |
+ | |
+void | |
+usage(char *argv0) | |
+{ | |
+ fprintf(stderr, "usage: %s file...\n", argv0); | |
+ exit(1); | |
+} | |
+ | |
+int | |
+main(int argc, char *argv[]) | |
+{ | |
+ if (argc == 1) | |
+ usage(argv[0]); | |
+ argc--; | |
+ argv++; | |
+ initassocs(); | |
+ for (; *argv != NULL; argv++) { | |
+ struct assoc *assoc; | |
+ | |
+ assoc = openwith(argv[0]); | |
+ if (assoc == NULL) | |
+ continue; | |
+ spawnassoc(assoc, argv[0]); | |
+ } | |
+ return 0; | |
+} | |
diff --git a/nopenconf.def.h b/nopenconf.def.h | |
@@ -0,0 +1,9 @@ | |
+/* See LICENSE file for copyright and license details. */ | |
+struct assoc assocs[] = { | |
+ { .regex = "\\.(avi|mp4|mkv|mp3|ogg|flac|mov)$", .file = "mpv", .argv … | |
+ { .regex = "\\.(png|jpg|gif)$", .file = "sxiv", .argv = { "sxiv", "{}"… | |
+ { .regex = "\\.(html|svg)$", .file = "firefox", .argv = { "firefox", "… | |
+ { .regex = "\\.pdf$", .file = "mupdf", .argv = { "mupdf", "{}", NULL} … | |
+ { .regex = "\\.sh$", .file = "sh", .argv = { "sh", "{}", NULL} }, | |
+ { .regex = ".", .file = "less", .argv = { "less", "{}", NULL } }, | |
+}; | |
diff --git a/spawn.c b/spawn.c | |
@@ -0,0 +1,43 @@ | |
+/* See LICENSE file for copyright and license details. */ | |
+#include <sys/types.h> | |
+#include <sys/wait.h> | |
+ | |
+#include <stdarg.h> | |
+#include <unistd.h> | |
+ | |
+#include "util.h" | |
+ | |
+void | |
+spawnvp(char *dir, char *file, char *argv[]) | |
+{ | |
+ pid_t pid; | |
+ int status; | |
+ | |
+ pid = fork(); | |
+ if (pid == 0) { | |
+ if (dir != NULL) | |
+ chdir(dir); | |
+ execvp(file, argv); | |
+ _exit(1); | |
+ } else { | |
+ /* Ignore interruptions */ | |
+ while (waitpid(pid, &status, 0) == -1) | |
+ ; | |
+ } | |
+} | |
+ | |
+void | |
+spawnlp(char *dir, char *file, char *argv0, ...) | |
+{ | |
+ char *argv[NR_ARGS]; | |
+ va_list ap; | |
+ int argc; | |
+ | |
+ va_start(ap, argv0); | |
+ argv[0] = argv0; | |
+ for (argc = 1; argv[argc] = va_arg(ap, char *); argc++) | |
+ ; | |
+ argv[argc] = NULL; | |
+ va_end(ap); | |
+ spawnvp(dir, file, argv); | |
+} | |
diff --git a/strverscmp.c b/strverscmp.c | |
@@ -1,6 +1,8 @@ | |
+/* See LICENSE file for copyright and license details. */ | |
#include <ctype.h> | |
#include <stdlib.h> | |
#include <string.h> | |
+ | |
#include "util.h" | |
int | |
diff --git a/util.h b/util.h | |
@@ -1,12 +1,6 @@ | |
/* See LICENSE file for copyright and license details. */ | |
-#undef strlcat | |
-size_t strlcat(char *, const char *, size_t); | |
-#undef strlcpy | |
-size_t strlcpy(char *, const char *, size_t); | |
-#undef dprintf | |
-int dprintf(int, const char *, ...); | |
-#undef strverscmp | |
-int strverscmp(const char *, const char *); | |
+#define LEN(x) (sizeof(x) / sizeof(*(x))) | |
+#define NR_ARGS 32 | |
#ifdef DEBUG | |
#define DEBUG_FD 8 | |
@@ -22,3 +16,14 @@ int strverscmp(const char *, const char *); | |
#define DPRINTF_P(x) | |
#define DPRINTF_LLU(x) | |
#endif /* DEBUG */ | |
+ | |
+#undef strlcat | |
+size_t strlcat(char *, const char *, size_t); | |
+#undef strlcpy | |
+size_t strlcpy(char *, const char *, size_t); | |
+#undef dprintf | |
+int dprintf(int, const char *, ...); | |
+ | |
+int strverscmp(const char *, const char *); | |
+void spawnvp(char *, char *, char *[]); | |
+void spawnlp(char *, char *, char *, ...); |