From: ori@eigenstate.org Subject: ksh: add OSC7 support To: tech@openbsd.org Date: Thu, 06 Nov 2025 20:23:26 -0500 I use OSC7 in order to advertise the working directory to the terminal emulator I use. While I can hack this together in ksh with some scripts, it feels like it would be a better fit to have directly in the shell, though I'm uncertain about that. It seems a bit odd to have support for this in tmux, but not emit it in our own shell. The patch below adds a '\O' option to PS1. It needs to be enabled explicitly, as: PS1=\O\h\$ I'd also considered printing it after every 'cd' command, either unconditionally or behind a knob. Is there interest in this? If there is, is this the right place to do it, or should we put it somewhere else? diff b0824ea26cb2a850adf7ef5a41c70bb06f83050c uncommitted --- a/bin/ksh/ksh.1 +++ b/bin/ksh/ksh.1 @@ -1618,6 +1618,10 @@ .Dv $HOME is abbreviated as .Sq ~ . +.It Sy \eO +The OSC-7 escape code. +This advertises the current working directory to +terminal emulator under wish ksh is running. .It Sy \e! The current history number. An unescaped @@ -4282,7 +4286,7 @@ .Ar n blocks on files written by the shell and its child processes (files of any size may be read). -.It Fl H +.It Fl// Set the hard limit only (the default is to set both hard and soft limits). .It Fl l Ar n Impose a limit of --- a/bin/ksh/lex.c +++ b/bin/ksh/lex.c @@ -1207,10 +1207,37 @@ } } +static char* +osc7encode(char *p, char *e, char *s, int raw) +{ + static const char hex[] = "0123456789abcdef"; + + while(*s){ + if((*s >= 'a' && *s <= 'z') + || (*s >= 'A' && *s <= 'f') + || (*s >= '0' && *s <= '9') + || (strchr("/-_.~", *s) != NULL) + || raw){ + if(e - p < 2) + break; + *p++ = *s++; + }else{ + if(e - p < 4) + break; + *p++ = '%'; + *p++ = hex[(*s>>4) & 0xf]; + *p++ = hex[(*s>>0) & 0xf]; + s++; + } + } + *p = 0; + return p; +} + static int dopprompt(const char *sp, int ntruncate, const char **spp, int doprint) { - char strbuf[1024], tmpbuf[1024], *p, *str, nbuf[32], delimiter = '\0'; + char strbuf[1024], tmpbuf[1024], *p, *e, *str, nbuf[32], delimiter = '\0'; int len, c, n, totlen = 0, indelimit = 0, counting = 1, delimitthis; const char *cp = sp; struct tm *tm; @@ -1398,6 +1425,20 @@ strbuf[1] = '\0'; } else strlcpy(strbuf, basename(p), sizeof strbuf); + break; + case 'O': + str = str_val(global("PWD")); + gethostname(tmpbuf, sizeof strbuf); + if((e = strchr(tmpbuf, '.')) != NULL) + *e = '\0'; + + p = strbuf; + e = strbuf + sizeof(strbuf); + p = osc7encode(p, e, "\033]7;file://", 1); + p = osc7encode(p, e, tmpbuf, 0); + p = osc7encode(p, e, "/", 0); + p = osc7encode(p, e, str, 0); + p = osc7encode(p, e, "\033", 1); break; case '!': /* '\' '!' history line number */ snprintf(strbuf, sizeof strbuf, "%d",