commit 0ea885df081beae2f2fc524ba9c5fb064078e9c4
parent 6ad7be2b64423374a60a94398762d4792899b7a1
Author: Bharatvaj Hemanth <bharatvaj@yahoo.com>
Date: Tue, 18 Feb 2025 18:00:44 +0530
Allow capture groups substitution with %1-%9
Diffstat:
A | .gitignore | | | 2 | ++ |
M | Makefile | | | 21 | +++++++++------------ |
A | README | | | 46 | ++++++++++++++++++++++++++++++++++++++++++++++ |
A | chndlr.c | | | 89 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
M | config.h | | | 19 | +++++++++++++++---- |
D | soap.c | | | 56 | -------------------------------------------------------- |
6 files changed, 161 insertions(+), 72 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -0,0 +1,2 @@
+chndlr
+chndlr.o
diff --git a/Makefile b/Makefile
@@ -1,14 +1,14 @@
-# soap - simple xdg-open replacement with fallback
+# chndlr - simple xdg-open replacement with fallback
include config.mk
-SRC = soap.c
+SRC = chndlr.c
OBJ = ${SRC:.c=.o}
-all: options soap
+all: options chndlr
options:
- @echo soap build options:
+ @echo chndlr build options:
@echo "CFLAGS = ${CFLAGS}"
@echo "LDFLAGS = ${LDFLAGS}"
@echo "CC = ${CC}"
@@ -17,22 +17,19 @@ options:
@echo CC $<
@${CC} -c ${CFLAGS} $<
-${OBJ}: config.mk
+${OBJ}: config.mk config.h
-soap: ${OBJ}
+chndlr: ${OBJ}
@echo CC -o $@
@${CC} -o $@ ${OBJ} ${LDFLAGS}
clean:
@echo cleaning
- @rm -f soap ${OBJ}
+ @rm -f chndlr ${OBJ}
install: all
- @test -f /usr/bin/xdg-open_ || (echo backing up to /usr/bin/xdg-open_; mv /usr/bin/xdg-open /usr/bin/xdg-open_)
- @echo installing new xdg-open
- @cp -f soap /usr/bin/xdg-open
+ @cp -f chndlr /usr/bin/xdg-open
@chmod 755 /usr/bin/xdg-open
uninstall:
- @echo moving xdg-open_ back into place
- @(test -f /usr/bin/xdg-open_ && mv /usr/bin/xdg-open_ /usr/bin/xdg-open) || echo ERROR: xdg-open_ does not exist
+ -rm /usr/bin/xdg-open
diff --git a/README b/README
@@ -0,0 +1,46 @@
+chndlr
+======
+
+chndlr is a simple, fast, and suckless replacement for xdg-open. It determines the appropriate application to open a file or URL based on user-defined rules in config.h.
+
+Features
+--------
+* Minimal and fast
+* No unnecessary dependencies
+* Proven config.h configuration
+* Works on UNIX-like systems
+
+Installation
+------------
+
+ $ make
+ $ sudo make install
+
+Usage
+-----
+
+ $ chndlr music.mp3
+ $ chndlr https://www.youtube.com/watch?v=dQw4w9WgXcQ
+
+config.h
+--------
+chndlr uses config.h for configuration,
+
+ static const Pair pairs[] = {
+ // regex action
+ ...
+ { "\\.mp3$", "st -e mplayer %s" },
+ { "(https://github.com)/([^/]+)/([^/]+)/actions",
+ "gh run list --repo %1%2/%3/%4" }
+ }
+
+In action, %1 captures the first group (), %2 the second and so on, until 9.
+%s substites the whole text.
+
+To know more the about capture group sytax, see regex(7).
+
+Credits
+-------
+Forked from soap.
+https://github.com/FRIGN/soap
+
diff --git a/chndlr.c b/chndlr.c
@@ -0,0 +1,89 @@
+/* See LICENSE file for copyright and license details. */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <regex.h>
+
+typedef struct {
+ const char *regex;
+ const char *action;
+} Pair;
+
+#include "config.h"
+
+int
+main(int argc, char *argv[]){
+ int g, h, i;
+ char cmd[BUFSIZ], sharg[BUFSIZ];
+ memset(cmd, '\0', BUFSIZ);
+ regex_t regex;
+ regmatch_t match[9];
+
+ /* we only take one argument */
+ if (argc != 2)
+ return EXIT_FAILURE;
+
+ /* make the argument shell-ready
+ * 1) start with '
+ * 2) escape ' to '\''
+ * 3) close with '\0
+ */
+ sharg[0] = '\'';
+ for (g=0, h=1; argv[1][g] && h < BUFSIZ-1-3-2; ++g, ++h) {
+ sharg[h] = argv[1][g];
+ if (argv[1][g] == '\'') {
+ sharg[++h] = '\\';
+ sharg[++h] = '\'';
+ sharg[++h] = '\'';
+ }
+ }
+ sharg[h] = '\'';
+ sharg[++h] = 0;
+
+ int len;
+ /* check regex and launch action if it matches argv[1] */
+ for (i=0; i < sizeof(pairs)/sizeof(*pairs); ++i) {
+ if (regcomp(®ex, pairs[i].regex, REG_EXTENDED))
+ fprintf(stderr, "invalid regex: %s\n", pairs[i].regex);
+
+ if (regexec(®ex, argv[1], 9, match, 0) == 0) {
+ const char *action = pairs[i].action;
+ char *p = cmd;
+
+ while (*action) {
+ /* check if N in %N is between 0 and 9 */
+ if (*action == '%'){
+ unsigned char nc = *(action + 1);
+ if (nc - '0' <= 9) {
+ int group = nc - '0';
+ len = match[group].rm_eo - match[group].rm_so;
+ snprintf(p, len + 1, "%.*s", len, argv[1] + match[group].rm_so);
+ } else /* if (nc == 's') */ {
+ len = strlen(argv[1]);
+ snprintf(p, len + 1, "%.*s", len, argv[1]);
+ }
+ action += 2;
+ p += len;
+ } else {
+ *p++ = *action++;
+ }
+ }
+ *p = '\0';
+
+ printf("Executing: %s\n", cmd);
+ system(cmd);
+ return EXIT_SUCCESS;
+ }
+ /*
+ else {
+ printf("Regex no match %s\n", pairs[i].regex);
+ }
+ */
+ regfree(®ex);
+ }
+
+ /* alternatively, fall back to SOAP_BROWSER */
+ snprintf(cmd, sizeof cmd, CHNDLR_FALLBACK_CMD " %s", sharg);
+ system(cmd);
+ return EXIT_SUCCESS;
+}
diff --git a/config.h b/config.h
@@ -1,9 +1,20 @@
/* See LICENSE file for copyright and license details. */
+#define CHNDLR_FALLBACK_CMD "firefox"
+
static const Pair pairs[] = {
/* regex action */
- { "\.(jpg|png|tiff)$", "feh %s" },
- { "\.gif$", "wget -O /tmp/tmp_gifview.gif %s && gifview -a /tmp/tmp_gifview.gif" },
- { "\.mp3$", "st -e mplayer %s" },
- { "^(http://|https://)?(www\.)?(youtube.com/watch\?|youtu\.be/)", "youtube-viewer %s" }
+ /* files */
+ { "\\.(jpg|png|tiff)$", "nsxiv %s" },
+ { "\\.gif$", "wget -O /tmp/tmp_gifview.gif %s && gifview -a /tmp/tmp_gifview.gif" },
+ { "\\.mp3$", "st -e mplayer %s" },
+
+ /* web */
+#define WEB_PREFIX "^(http|https)://?(www\.)?"
+ /* youtube */
+ { WEB_PREFIX "(youtube.com/watch\?|youtu\.be/)", "mpv %s" },
+
+ /* github */
+ { WEB_PREFIX "(github.com)/([^/]+)/([^/]+)/actions", "gh run list --repo %1://%2%3/%4/%5" },
+ { WEB_PREFIX "(github.com)", "lynx %s" },
};
diff --git a/soap.c b/soap.c
@@ -1,56 +0,0 @@
-/* See LICENSE file for copyright and license details. */
-#include <stdlib.h>
-#include <stdio.h>
-#include <regex.h>
-
-typedef struct {
- const char *regex;
- const char *action;
-} Pair;
-
-#include "config.h"
-
-int
-main(int argc, char *argv[]){
- int g, h, i;
- char cmd[BUFSIZ], sharg[BUFSIZ];
- regex_t regex;
-
- /* we only take one argument */
- if (argc != 2)
- return EXIT_FAILURE;
-
- /* make the argument shell-ready
- * 1) start with '
- * 2) escape ' to '\''
- * 3) close with '\0
- */
- sharg[0] = '\'';
- for (g=0, h=1; argv[1][g] && h < BUFSIZ-1-3-2; ++g, ++h) {
- sharg[h] = argv[1][g];
- if (argv[1][g] == '\'') {
- sharg[++h] = '\\';
- sharg[++h] = '\'';
- sharg[++h] = '\'';
- }
- }
- sharg[h] = '\'';
- sharg[++h] = 0;
-
- /* check regex and launch action if it matches argv[1] */
- for (i=0; i < sizeof(pairs)/sizeof(*pairs); ++i) {
- if (regcomp(®ex, pairs[i].regex, REG_EXTENDED))
- fprintf(stderr, "invalid regex: %s\n", pairs[i].regex);
- if (!regexec(®ex, argv[1], 0, NULL, 0)) {
- snprintf(cmd, sizeof cmd, pairs[i].action, sharg);
- system(cmd);
- return EXIT_SUCCESS;
- }
- regfree(®ex);
- }
-
- /* alternatively, fall back to xdg-open_ */
- snprintf(cmd, sizeof cmd, "xdg-open_ %s", sharg);
- system(cmd);
- return EXIT_SUCCESS;
-}