commit c4c5113fbdfb97363c510abb89a8da7f4fb138b2
parent 3f1a5ed034b65e8d9542e506bb7e85ed1fd4303c
Author: Utkarsh Verma <utkarsh@bitbanged.com>
Date: Tue, 15 Oct 2024 09:54:45 +0200
osc7: initial patch implementation (#154)
* osc7: initial patch implementation
Closes #153
* osc7: avoid redundant use of realpath()
* osc7: fix styling
* Changing position of the OSC7_PATCH toggle in patches.def.h
---------
Co-authored-by: Bakkeby <bakkeby@gmail.com>
Diffstat:
9 files changed, 136 insertions(+), 13 deletions(-)
diff --git a/patch/newterm.c b/patch/newterm.c
@@ -1,7 +1,15 @@
+extern char* argv0;
+
+static char*
+getcwd_by_pid(pid_t pid) {
+ static char cwd[32];
+ snprintf(cwd, sizeof cwd, "/proc/%d/cwd", pid);
+ return cwd;
+}
+
void
newterm(const Arg* a)
{
- int res;
switch (fork()) {
case -1:
die("fork failed: %s\n", strerror(errno));
@@ -12,9 +20,23 @@ newterm(const Arg* a)
die("fork failed: %s\n", strerror(errno));
break;
case 0:
- res = chdir(getcwd_by_pid(pid));
- execlp("st", "./st", NULL);
- break;
+ #if OSC7_PATCH
+ if (term.cwd) {
+ if (chdir(term.cwd) == 0) {
+ /* We need to put the working directory also in PWD, so that
+ * the shell starts in the right directory if `cwd` is a
+ * symlink. */
+ setenv("PWD", term.cwd, 1);
+ }
+ } else {
+ chdir(getcwd_by_pid(pid));
+ }
+ #else
+ chdir(getcwd_by_pid(pid));
+ #endif // OSC7_PATCH
+
+ execl("/proc/self/exe", argv0, NULL);
+ exit(1);
default:
exit(0);
}
@@ -22,9 +44,3 @@ newterm(const Arg* a)
wait(NULL);
}
}
-
-static char *getcwd_by_pid(pid_t pid) {
- char buf[32];
- snprintf(buf, sizeof buf, "/proc/%d/cwd", pid);
- return realpath(buf, NULL);
-}
-\ No newline at end of file
diff --git a/patch/newterm.h b/patch/newterm.h
@@ -1,2 +1 @@
void newterm(const Arg *);
-static char *getcwd_by_pid(pid_t pid);
-\ No newline at end of file
diff --git a/patch/osc7.c b/patch/osc7.c
@@ -0,0 +1,74 @@
+static int
+hex2int(char c)
+{
+ if (c >= '0' && c <= '9')
+ return c - '0';
+ else if (c >= 'a' && c <= 'f')
+ return c - 'a' + 10;
+ else if (c >= 'A' && c <= 'F')
+ return c - 'A' + 10;
+ else
+ return -1;
+}
+
+int
+osc7parsecwd(const char *uri)
+{
+ const char *auth, *host, *hostend;
+ char *path, decoded[PATH_MAX], thishost[_POSIX_HOST_NAME_MAX];
+ size_t i, decodedlen, hostlen, urilen;
+ int h1, h2;
+ if (!term.cwd) {
+ term.cwd = xmalloc(sizeof(decoded));
+ term.cwd[0] = '\0';
+ }
+ /* reset cwd if uri is empty */
+ if ((urilen = strlen(uri)) == 0) {
+ term.cwd[0] = '\0';
+ return 1;
+ }
+ /* decode uri */
+ for (decodedlen = 0, i = 0; i < urilen; i++) {
+ if (uri[i] == '%' && i <= urilen-3 &&
+ (h1 = hex2int(uri[i+1])) >= 0 && (h2 = hex2int(uri[i+2])) >= 0) {
+ decoded[decodedlen++] = (h1 << 4) | h2;
+ i += 2;
+ } else {
+ decoded[decodedlen++] = uri[i];
+ }
+ if (decodedlen == sizeof(decoded)) {
+ fprintf(stderr, "erresc (OSC 7): uri is too long\n");
+ return 0;
+ }
+ }
+ decoded[decodedlen] = '\0';
+ /* check scheme */
+ if (decodedlen < 5 || strncmp("file:", decoded, 5) != 0) {
+ fprintf(stderr, "erresc (OSC 7): scheme is not supported: '%s'\n", uri);
+ return 0;
+ }
+ /* find start of authority */
+ if (decodedlen < 7 || decoded[5] != '/' || decoded[6] != '/') {
+ fprintf(stderr, "erresc (OSC 7): invalid uri: '%s'\n", uri);
+ return 0;
+ }
+ auth = decoded + 7;
+ /* find start of path and reset cwd if path is missing */
+ if ((path = strchr(auth, '/')) == NULL) {
+ term.cwd[0] = '\0';
+ return 1;
+ }
+ /* ignore path if host is not localhost */
+ host = ((host = memchr(auth, '@', path - auth)) != NULL) ? host+1 : auth;
+ hostend = ((hostend = memchr(host, ':', path - host)) != NULL) ? hostend : path;
+ hostlen = hostend - host;
+ if (gethostname(thishost, sizeof(thishost)) < 0)
+ thishost[0] = '\0';
+ if (hostlen > 0 &&
+ !(hostlen == 9 && strncmp("localhost", host, hostlen) == 0) &&
+ !(hostlen == strlen(thishost) && strncmp(host, thishost, hostlen) == 0)) {
+ return 0;
+ }
+ memcpy(term.cwd, path, decodedlen - (path - decoded) + 1);
+ return 1;
+}
diff --git a/patch/osc7.h b/patch/osc7.h
@@ -0,0 +1 @@
+static int osc7parsecwd(const char *);
diff --git a/patch/st_include.c b/patch/st_include.c
@@ -27,3 +27,6 @@
#if SYNC_PATCH
#include "sync.c"
#endif
+#if OSC7_PATCH
+#include "osc7.c"
+#endif
diff --git a/patch/st_include.h b/patch/st_include.h
@@ -30,3 +30,6 @@
#if SYNC_PATCH
#include "sync.h"
#endif
+#if OSC7_PATCH
+#include "osc7.h"
+#endif
diff --git a/patches.def.h b/patches.def.h
@@ -301,6 +301,14 @@
*/
#define OPENURLONCLICK_PATCH 0
+/* This patch allows st to fetch the current working directory through the OSC 7 escape
+ * sequence emitted by shells. Must be used with newterm patch.
+ *
+ * https://codeberg.org/dnkl/foot/wiki#spawning-new-terminal-instances-in-the-current-working-directory
+ * https://github.com/veltza/st-sx/commit/817865c2c6ed905af8849580e58bdcf399216fbd
+ */
+#define OSC7_PATCH 0
+
/* This patch allows jumping between prompts by utilizing the OSC 133 escape sequence
* emitted by shells. Must be used with either reflow or scrollback patch.
*
diff --git a/st.c b/st.c
@@ -2654,6 +2654,11 @@ strhandle(void)
}
}
return;
+ #if OSC7_PATCH
+ case 7:
+ osc7parsecwd((const char *)strescseq.args[1]);
+ return;
+ #endif // OSC7_PATCH
case 8: /* Clear Hyperlinks */
return;
case 10:
@@ -2856,6 +2861,19 @@ strparse(void)
if (*p == '\0')
return;
+ /* preserve semicolons in window titles, icon names and OSC 7 sequences */
+ if (strescseq.type == ']' && (
+ p[0] <= '2'
+ #if OSC7_PATCH
+ || p[0] == '7'
+ #endif // OSC7_PATCH
+ ) && p[1] == ';') {
+ strescseq.args[strescseq.narg++] = p;
+ strescseq.args[strescseq.narg++] = p + 2;
+ p[1] = '\0';
+ return;
+ }
+
while (strescseq.narg < STR_ARG_SIZ) {
strescseq.args[strescseq.narg++] = p;
while ((c = *p) != ';' && c != '\0')
diff --git a/st.h b/st.h
@@ -205,6 +205,9 @@ typedef struct {
ImageList *images_alt; /* sixel images for alternate screen */
#endif // SIXEL_PATCH
Rune lastc; /* last printed char outside of sequence, 0 if control */
+ #if OSC7_PATCH
+ char* cwd; /* current working directory */
+ #endif // OSC7_PATCH
} Term;
typedef union {