This is an automated email from the git hooks/post-receive script.
git pushed a commit to branch master
in repository terminology.
View the commit online.
commit 2088547268a593d229e8b2a5b95691d319388519
Author: Boris Faure <[email protected]>
AuthorDate: Sat Sep 19 16:17:15 2026 +0000
termptyesc: handle OSC 7
Store the working directory the shell reports through
OSC 7 ; file://<host>/<path> ST in prop.cwd, beside title and icon.
---
src/bin/meson.build | 2 ++
src/bin/termpty.c | 1 +
src/bin/termpty.h | 2 ++
src/bin/termptyesc.c | 97 ++++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 102 insertions(+)
diff --git a/src/bin/meson.build b/src/bin/meson.build
index 0962a6a3..a68db65c 100644
--- a/src/bin/meson.build
+++ b/src/bin/meson.build
@@ -71,6 +71,7 @@ tyfuzz_sources = ['termptyesc.c', 'termptyesc.h',
'utils.c', 'utils.h',
'utf8.c', 'utf8.h',
simd_sources,
+ 'uri_decode.c', 'uri_decode.h',
'tytest_common.c', 'tytest_common.h',
'tyfuzz.c']
tytest_sources = ['termptyesc.c', 'termptyesc.h',
@@ -112,6 +113,7 @@ tybench_sources = ['termptyesc.c', 'termptyesc.h',
'utils.c', 'utils.h',
'utf8.c', 'utf8.h',
simd_sources,
+ 'uri_decode.c', 'uri_decode.h',
'tytest_common.c', 'tytest_common.h',
'tybench.c']
diff --git a/src/bin/termpty.c b/src/bin/termpty.c
index 09d501c8..a2b2c4e3 100644
--- a/src/bin/termpty.c
+++ b/src/bin/termpty.c
@@ -893,6 +893,7 @@ termpty_free(Termpty *ty)
eina_stringshare_del(ty->prop.title);
eina_stringshare_del(ty->prop.user_title);
eina_stringshare_del(ty->prop.icon);
+ eina_stringshare_del(ty->prop.cwd);
termpty_backlog_free(ty);
free(ty->screen);
free(ty->screen2);
diff --git a/src/bin/termpty.h b/src/bin/termpty.h
index bee5cfdd..b9ccee35 100644
--- a/src/bin/termpty.h
+++ b/src/bin/termpty.h
@@ -175,6 +175,8 @@ struct tag_Termpty
const char *title;
/* set by user */
const char *user_title;
+ /* working directory reported by the shell through OSC 7 */
+ const char *cwd;
} prop;
const char *cur_cmd;
Termcell *screen, *screen2;
diff --git a/src/bin/termptyesc.c b/src/bin/termptyesc.c
index e33ca621..0457dba3 100644
--- a/src/bin/termptyesc.c
+++ b/src/bin/termptyesc.c
@@ -14,6 +14,9 @@
#include "tytest.h"
#endif
#include "utils.h"
+#include "uri_decode.h"
+#include <limits.h>
+#include <unistd.h>
#undef CRITICAL
#undef ERR
@@ -4628,6 +4631,82 @@ err:
ty->decoding_error = EINA_TRUE;
}
+/* Only an empty host, "localhost" or our own name names a directory we can
+ * actually reach. */
+static Eina_Bool
+_osc_7_host_is_local(const char *host, size_t len)
+{
+ static char hostname[256];
+ static Eina_Bool hostname_known = EINA_FALSE;
+
+ if (len == 0)
+ return EINA_TRUE;
+ if ((len == 9) && (strncasecmp(host, "localhost", 9) == 0))
+ return EINA_TRUE;
+
+ if (!hostname_known)
+ {
+ if (gethostname(hostname, sizeof(hostname)) != 0)
+ hostname[0] = '\0';
+ hostname[sizeof(hostname) - 1] = '\0';
+ hostname_known = EINA_TRUE;
+ }
+ return (hostname[0] != '\0') && (strlen(hostname) == len) &&
+ (strncasecmp(host, hostname, len) == 0);
+}
+
+/* OSC 7 ; file://<host>/<percent-encoded path> ST
+ *
+ * Validated here, at the only ingress, because the consumers are spread out
+ * and the next one written will not necessarily be careful.
+ */
+static void
+_handle_osc_7_cwd(Termpty *ty, char *uri)
+{
+ char *path;
+ size_t len, i;
+
+ if (strncmp(uri, "file://", 7) != 0)
+ {
+ WRN("OSC 7: not a file:// URI");
+ goto err;
+ }
+ uri += 7;
+ path = strchr(uri, '/');
+ if (!path)
+ {
+ WRN("OSC 7: URI has no path");
+ goto err;
+ }
+
+ /* A remote shell announcing its own host is behaving correctly. We simply
+ * have nothing to do with its paths, so drop it without an error. */
+ if (!_osc_7_host_is_local(uri, path - uri))
+ return;
+
+ uri_percent_decode_inplace(path);
+ len = strlen(path);
+ if (len >= PATH_MAX)
+ {
+ WRN("OSC 7: path too long (%zu bytes)", len);
+ goto err;
+ }
+ for (i = 0; i < len; i++)
+ {
+ if (((unsigned char)path[i] < 0x20) || (path[i] == 0x7f))
+ {
+ WRN("OSC 7: control character in path");
+ goto err;
+ }
+ }
+
+ eina_stringshare_replace(&ty->prop.cwd, path);
+ return;
+
+err:
+ ty->decoding_error = EINA_TRUE;
+}
+
static int
_handle_esc_osc(Termpty *ty, const Eina_Unicode *c, const Eina_Unicode *ce)
{
@@ -4738,6 +4817,24 @@ _handle_esc_osc(Termpty *ty, const Eina_Unicode *c, const Eina_Unicode *ce)
if ((cc - c) < 3)
return 0;
break;
+ case 7:
+ DBG("set working directory");
+ if (!p)
+ goto err;
+ /* tmux re-emits the active pane's path, and sends it empty when the
+ * pane has none. That means "forget it", not a malformed sequence. */
+ if (!*p)
+ {
+ eina_stringshare_replace(&ty->prop.cwd, NULL);
+ break;
+ }
+ s = eina_unicode_unicode_to_utf8(p, &len);
+ if (s)
+ {
+ _handle_osc_7_cwd(ty, s);
+ free(s);
+ }
+ break;
case 8:
DBG("hyperlink");
if (!p || !*p)
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.