chndlr

Personal fork of spm (simple password manager)

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
6 files changed, 161 insertions(+), 72 deletions(-)
A
.gitignore
|
2
++
M
Makefile
|
21
+++++++++------------
A
README
|
46
++++++++++++++++++++++++++++++++++++++++++++++
A
chndlr.c
|
89
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
M
config.h
|
19
+++++++++++++++----
D
soap.c
|
56
--------------------------------------------------------
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(&regex, pairs[i].regex, REG_EXTENDED))
+			fprintf(stderr, "invalid regex: %s\n", pairs[i].regex);
+
+		if (regexec(&regex, 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(&regex);
+	}
+
+	/* 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(&regex, pairs[i].regex, REG_EXTENDED))
-			fprintf(stderr, "invalid regex: %s\n", pairs[i].regex);
-		if (!regexec(&regex, argv[1], 0, NULL, 0)) {
-			snprintf(cmd, sizeof cmd, pairs[i].action, sharg);
-			system(cmd);
-			return EXIT_SUCCESS;
-		}
-		regfree(&regex);
-	}
-
-	/* alternatively, fall back to xdg-open_ */
-	snprintf(cmd, sizeof cmd, "xdg-open_ %s", sharg);
-	system(cmd);
-	return EXIT_SUCCESS;
-}