From 5d51b613de66ab2eb312ca4184b8f84e51ecb1fb Mon Sep 17 00:00:00 2001 From: ProsperousPotato Date: Thu, 22 Jan 2026 21:36:00 +0000 Subject: add url bindings --- Makefile | 4 +++ config.h | 7 ++++++ config.mk | 2 +- st-copyout | 15 +++++++++++ st-urlhandler | 22 ++++++++++++++++ st.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- st.h | 1 + 7 files changed, 128 insertions(+), 3 deletions(-) create mode 100755 st-copyout create mode 100644 st-urlhandler diff --git a/Makefile b/Makefile index 15db421..fb24e97 100644 --- a/Makefile +++ b/Makefile @@ -37,7 +37,11 @@ dist: clean install: st mkdir -p $(DESTDIR)$(PREFIX)/bin cp -f st $(DESTDIR)$(PREFIX)/bin + cp -f st-copyout $(DESTDIR)$(PREFIX)/bin + cp -f st-urlhandler $(DESTDIR)$(PREFIX)/bin chmod 755 $(DESTDIR)$(PREFIX)/bin/st + chmod 755 $(DESTDIR)$(PREFIX)/bin/st-copyout + chmod 755 $(DESTDIR)$(PREFIX)/bin/st-urlhandler mkdir -p $(DESTDIR)$(MANPREFIX)/man1 sed "s/VERSION/$(VERSION)/g" < st.1 > $(DESTDIR)$(MANPREFIX)/man1/st.1 chmod 644 $(DESTDIR)$(MANPREFIX)/man1/st.1 diff --git a/config.h b/config.h index e28d462..9c038f5 100644 --- a/config.h +++ b/config.h @@ -216,6 +216,10 @@ static MouseShortcut mshortcuts[] = { { XK_ANY_MOD, Button5, ttysend, {.s = "\005"} }, }; +static char *openurlcmd[] = { "/bin/sh", "-c", "st-urlhandler -o", "externalpipe", NULL }; +static char *copyurlcmd[] = { "/bin/sh", "-c", "st-urlhandler -c", "externalpipe", NULL }; +static char *copyoutput[] = { "/bin/sh", "-c", "st-copyout", "externalpipe", NULL }; + static Shortcut shortcuts[] = { /* mask keysym function argument */ { XK_ANY_MOD, XK_Break, sendbreak, {.i = 0} }, @@ -234,6 +238,9 @@ static Shortcut shortcuts[] = { { MODKEY, XK_d, kscrolldown, {.i = -1} }, { MODKEY, XK_k, kscrollup, {.i = +1} }, { MODKEY, XK_j, kscrolldown, {.i = +1} }, + { MODKEY, XK_l, externalpipe, {.v = openurlcmd } }, + { MODKEY, XK_y, externalpipe, {.v = copyurlcmd } }, + { MODKEY, XK_o, externalpipe, {.v = copyoutput } }, }; /* diff --git a/config.mk b/config.mk index 46c846c..1b3179c 100644 --- a/config.mk +++ b/config.mk @@ -22,7 +22,7 @@ LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft \ # flags STCPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600 -STCFLAGS = $(INCS) $(STCPPFLAGS) $(CPPFLAGS) $(CFLAGS) -O3 +STCFLAGS = $(INCS) $(STCPPFLAGS) $(CPPFLAGS) $(CFLAGS) -O3 -flto STLDFLAGS = $(LIBS) $(LDFLAGS) # OpenBSD: diff --git a/st-copyout b/st-copyout new file mode 100755 index 0000000..6f5a182 --- /dev/null +++ b/st-copyout @@ -0,0 +1,15 @@ +#!/bin/sh +# Using external pipe with st, give a dmenu prompt of recent commands, +# allowing the user to copy the output of one. +# xclip required for this script. +# By Jaywalker and Luke +# https://github.com/LukeSmithxyz/st + +tmpfile=$(mktemp /tmp/st-cmd-output.XXXXXX) +trap 'rm "$tmpfile"' 0 1 15 +sed -n "w $tmpfile" +sed -i 's/\x0//g' "$tmpfile" +ps1="$(grep "\S" "$tmpfile" | tail -n 1 | sed 's/^\s*//' | cut -d' ' -f1)" +chosen="$(grep -F "$ps1" "$tmpfile" | sed '$ d' | tac | dmenu -vi -p "Copy which command's output?" -i -l 10 | sed 's/[^^]/[&]/g; s/\^/\\^/g')" +eps1="$(echo "$ps1" | sed 's/[^^]/[&]/g; s/\^/\\^/g')" +awk "/^$chosen$/{p=1;print;next} p&&/$eps1/{p=0};p" "$tmpfile" | xclip -selection clipboard diff --git a/st-urlhandler b/st-urlhandler new file mode 100644 index 0000000..c369fb3 --- /dev/null +++ b/st-urlhandler @@ -0,0 +1,22 @@ +#!/bin/sh +# https://github.com/LukeSmithxyz/st + +BROWSER="glide" + +urlregex="(((http|https|gopher|gemini|ftp|ftps|git)://|www\\.)[a-zA-Z0-9.]*[:;a-zA-Z0-9./+@$&%?$\#=_~-]*)|((magnet:\\?xt=urn:btih:)[a-zA-Z0-9]*)" + +urls="$(sed 's/.*│//g' | tr -d '\n' | # First remove linebreaks and mutt sidebars: + grep -aEo "$urlregex" | # grep only urls as defined above. + uniq | # Ignore neighboring duplicates. + sed "s/\(\.\|,\|;\|\!\\|\?\)$//; + s/^www./http:\/\/www\./")" # xdg-open will not detect url without http + +[ -z "$urls" ] && exit 1 + +while getopts "hoc" o; do case "${o}" in + h) printf "Optional arguments for custom use:\\n -c: copy\\n -o: ${BROWSER}\\n -h: Show this message\\n" && exit 1 ;; + o) chosen="$(echo "$urls" | dmenu -vi -i -p 'Follow which url?' -l 10)" + setsid $BROWSER "$chosen" >/dev/null 2>&1 & ;; + c) echo "$urls" | dmenu -vi -i -p 'Copy which url?' -l 10 | tr -d '\n' | xclip -selection clipboard ;; + *) printf "Invalid option: -%s\\n" "$OPTARG" && exit 1 ;; +esac done diff --git a/st.c b/st.c index e0da67d..ee8bc97 100644 --- a/st.c +++ b/st.c @@ -48,6 +48,7 @@ (y) < term.scr ? term.hist[(term.histi + (y) - term.scr + 1 + HISTSIZE) % HISTSIZE] \ : term.line[(y) - term.scr] \ ) +#define TLINE_HIST(y) ((y) <= HISTSIZE-term.row+2 ? term.hist[(y)] : term.line[(y-HISTSIZE+term.row-3)]) #define TLINEABS(y) ( \ (y) < 0 ? term.hist[(term.histi + (y) + 1 + HISTSIZE) % HISTSIZE] : term.line[(y)] \ @@ -488,6 +489,20 @@ tgetline(char *buf, const Glyph *fgp) return ptr - buf; } +int +tlinehistlen(int y) +{ + int i = term.col; + + if (TLINE_HIST(y)[i - 1].mode & ATTR_WRAP) + return i; + + while (i > 0 && TLINE_HIST(y)[i - 1].u == ' ') + --i; + + return i; +} + void selstart(int col, int row, int snap) { @@ -794,8 +809,14 @@ sigchld(int a) if ((p = waitpid(pid, &stat, WNOHANG)) < 0) die("waiting for pid %hd failed: %s\n", pid, strerror(errno)); - if (pid != p) + if (pid != p) { + if (p == 0 && wait(&stat) < 0) + die("wait: %s\n", strerror(errno)); + + /* reinstall sigchld handler */ + signal(SIGCHLD, sigchld); return; + } if (WIFEXITED(stat) && WEXITSTATUS(stat)) die("child exited with status %d\n", WEXITSTATUS(stat)); @@ -879,7 +900,7 @@ ttynew(const char *line, char *cmd, const char *out, char **args) break; default: #ifdef __OpenBSD__ - if (pledge("stdio rpath tty proc", NULL) == -1) + if (pledge("stdio rpath tty proc exec", NULL) == -1) die("pledge\n"); #endif close(s); @@ -2246,6 +2267,61 @@ strparse(void) } } +void +externalpipe(const Arg *arg) +{ + int to[2]; + char buf[UTF_SIZ]; + void (*oldsigpipe)(int); + Glyph *bp, *end; + int lastpos, n, newline; + + if (pipe(to) == -1) + return; + + switch (fork()) { + case -1: + close(to[0]); + close(to[1]); + return; + case 0: + dup2(to[0], STDIN_FILENO); + close(to[0]); + close(to[1]); + execvp(((char **)arg->v)[0], (char **)arg->v); + fprintf(stderr, "st: execvp %s\n", ((char **)arg->v)[0]); + perror("failed"); + exit(0); + } + + close(to[0]); + /* ignore sigpipe for now, in case child exists early */ + oldsigpipe = signal(SIGPIPE, SIG_IGN); + newline = 0; + for (n = 0; n <= HISTSIZE + 2; n++) { + bp = TLINE_HIST(n); + lastpos = MIN(tlinehistlen(n) + 1, term.col) - 1; + if (lastpos < 0) + break; + if (lastpos == 0) + continue; + end = &bp[lastpos + 1]; + for (; bp < end; ++bp) + if (xwrite(to[1], buf, utf8encode(bp->u, buf)) < 0) + break; + if ((newline = TLINE_HIST(n)[lastpos].mode & ATTR_WRAP)) + continue; + if (xwrite(to[1], "\n", 1) < 0) + break; + newline = 0; + } + if (newline) + (void)xwrite(to[1], "\n", 1); + close(to[1]); + /* restore */ + signal(SIGPIPE, oldsigpipe); +} + void strdump(void) { diff --git a/st.h b/st.h index 514ec08..d355538 100644 --- a/st.h +++ b/st.h @@ -85,6 +85,7 @@ void draw(void); void kscrolldown(const Arg *); void kscrollup(const Arg *); +void externalpipe(const Arg *); void printscreen(const Arg *); void printsel(const Arg *); void sendbreak(const Arg *); -- cgit v1.2.3