X-Git-Url: https://git.xinqibao.xyz/st.git/blobdiff_plain/aaef13aaaaddbbf1e793b9df0e9cfc4d6d28d1b9..588ba51a539d18de5f9497a38ab7875e7b3e1429:/st.c

diff --git a/st.c b/st.c
index b9bab29..9ad4f89 100644
--- a/st.c
+++ b/st.c
@@ -43,9 +43,10 @@
 #define XEMBED_FOCUS_OUT 5
 
 /* Arbitrary sizes */
-#define ESC_TITLE_SIZ 256
 #define ESC_BUF_SIZ   256
 #define ESC_ARG_SIZ   16
+#define STR_BUF_SIZ   256
+#define STR_ARG_SIZ   16
 #define DRAW_BUF_SIZ  1024
 #define UTF_SIZ       4
 #define XK_NO_MOD     UINT_MAX
@@ -96,11 +97,11 @@ enum glyph_state {
 };
 
 enum term_mode {
-	MODE_WRAP        = 1,
+	MODE_WRAP	= 1,
 	MODE_INSERT      = 2,
 	MODE_APPKEYPAD   = 4,
 	MODE_ALTSCREEN   = 8,
-	MODE_CRLF        = 16,
+	MODE_CRLF	= 16,
 	MODE_MOUSEBTN    = 32,
 	MODE_MOUSEMOTION = 64,
 	MODE_MOUSE       = 32|64,
@@ -109,10 +110,10 @@ enum term_mode {
 
 enum escape_state {
 	ESC_START      = 1,
-	ESC_CSI        = 2,
-	ESC_OSC        = 4,
-	ESC_TITLE      = 8,
-	ESC_ALTCHARSET = 16
+	ESC_CSI	= 2,
+	ESC_STR	= 4, /* DSC, OSC, PM, APC */
+	ESC_ALTCHARSET = 8,
+	ESC_STR_END    = 16, /* a final string was encountered */
 };
 
 enum window_state {
@@ -151,13 +152,23 @@ typedef struct {
 /* ESC '[' [[ [<priv>] <arg> [;]] <mode>] */
 typedef struct {
 	char buf[ESC_BUF_SIZ]; /* raw string */
-	int len;               /* raw string length */
+	int len;	       /* raw string length */
 	char priv;
 	int arg[ESC_ARG_SIZ];
-	int narg;              /* nb of args */
+	int narg;	      /* nb of args */
 	char mode;
 } CSIEscape;
 
+/* STR Escape sequence structs */
+/* ESC type [[ [<priv>] <arg> [;]] <mode>] ESC '\' */
+typedef struct {
+	char type;	     /* ESC type ... */
+	char buf[STR_BUF_SIZ]; /* raw string */
+	int len;	       /* raw string length */
+	char *args[STR_ARG_SIZ];
+	int narg;	      /* nb of args */
+} STREscape;
+
 /* Internal representation of the screen */
 typedef struct {
 	int row;	/* nb row */
@@ -170,8 +181,6 @@ typedef struct {
 	int bot;	/* bottom scroll limit */
 	int mode;	/* terminal mode flags */
 	int esc;	/* escape state flags */
-	char title[ESC_TITLE_SIZ];
-	int titlelen;
 	bool *tabs;
 } Term;
 
@@ -239,6 +248,10 @@ static void csidump(void);
 static void csihandle(void);
 static void csiparse(void);
 static void csireset(void);
+static void strdump(void);
+static void strhandle(void);
+static void strparse(void);
+static void strreset(void);
 
 static void tclearregion(int, int, int, int);
 static void tcursor(int);
@@ -249,7 +262,7 @@ static void tinsertblankline(int);
 static void tmoveto(int, int);
 static void tnew(int, int);
 static void tnewline(int);
-static void tputtab(void);
+static void tputtab(bool);
 static void tputc(char*);
 static void treset(void);
 static int tresize(int, int);
@@ -260,6 +273,7 @@ static void tsetchar(char*);
 static void tsetscroll(int, int);
 static void tswapscreen(void);
 static void tsetdirt(int, int);
+static void tsetmode(bool, bool, int *, int);
 static void tfulldirt(void);
 
 static void ttynew(void);
@@ -323,7 +337,8 @@ static void (*handler[LASTEvent])(XEvent *) = {
 static DC dc;
 static XWindow xw;
 static Term term;
-static CSIEscape escseq;
+static CSIEscape csiescseq;
+static STREscape strescseq;
 static int cmdfd;
 static pid_t pid;
 static Selection sel;
@@ -968,22 +983,22 @@ tnewline(int first_col) {
 void
 csiparse(void) {
 	/* int noarg = 1; */
-	char *p = escseq.buf;
+	char *p = csiescseq.buf;
 
-	escseq.narg = 0;
+	csiescseq.narg = 0;
 	if(*p == '?')
-		escseq.priv = 1, p++;
+		csiescseq.priv = 1, p++;
 	
-	while(p < escseq.buf+escseq.len) {
+	while(p < csiescseq.buf+csiescseq.len) {
 		while(isdigit(*p)) {
-			escseq.arg[escseq.narg] *= 10;
-			escseq.arg[escseq.narg] += *p++ - '0'/*, noarg = 0 */;
+			csiescseq.arg[csiescseq.narg] *= 10;
+			csiescseq.arg[csiescseq.narg] += *p++ - '0'/*, noarg = 0 */;
 		}
-		if(*p == ';' && escseq.narg+1 < ESC_ARG_SIZ)
-			escseq.narg++, p++;
+		if(*p == ';' && csiescseq.narg+1 < ESC_ARG_SIZ)
+			csiescseq.narg++, p++;
 		else {
-			escseq.mode = *p;
-			escseq.narg++;
+			csiescseq.mode = *p;
+			csiescseq.narg++;
 			return;
 		}
 	}
@@ -1113,7 +1128,7 @@ tsetattr(int *attr, int l) {
 					fprintf(stderr, "erresc: bad fgcolor %d\n", attr[i]);
 			}
 			else
-				fprintf(stderr, "erresc: gfx attr %d unknown\n", attr[i]);
+				fprintf(stderr, "erresc(38): gfx attr %d unknown\n", attr[i]);
 			break;
 		case 39:
 			term.c.attr.fg = DefaultFG;
@@ -1127,7 +1142,7 @@ tsetattr(int *attr, int l) {
 					fprintf(stderr, "erresc: bad bgcolor %d\n", attr[i]);
 			}
 			else
-				fprintf(stderr, "erresc: gfx attr %d unknown\n", attr[i]);
+				fprintf(stderr, "erresc(48): gfx attr %d unknown\n", attr[i]);
 			break;
 		case 49:
 			term.c.attr.bg = DefaultBG;
@@ -1142,8 +1157,7 @@ tsetattr(int *attr, int l) {
 			else if(BETWEEN(attr[i], 100, 107))
 				term.c.attr.fg = attr[i] - 100 + 8;
 			else
-				fprintf(stderr, "erresc: gfx attr %d unknown\n", attr[i]), csidump();
-			
+				fprintf(stderr, "erresc(default): gfx attr %d unknown\n", attr[i]), csidump();
 			break;
 		}
 	}
@@ -1164,9 +1178,82 @@ tsetscroll(int t, int b) {
 	term.bot = b;
 }
 
+#define MODBIT(x, set, bit) ((set) ? ((x) |= (bit)) : ((x) &= ~(bit)))
+
+void
+tsetmode(bool priv, bool set, int *args, int narg) {
+	int *lim, mode;
+
+	for (lim = args + narg; args < lim; ++args) {
+		if(priv) {
+			switch(*args) {
+			case 1:
+				MODBIT(term.mode, set, MODE_APPKEYPAD);
+				break;
+			case 5: /* DECSCNM -- Reverve video */
+				mode = term.mode;
+				MODBIT(term.mode,set, MODE_REVERSE);
+				if (mode != term.mode)
+					draw();
+				break;
+			case 7:
+				MODBIT(term.mode, set, MODE_WRAP);
+				break;
+			case 20:
+				MODBIT(term.mode, set, MODE_CRLF);
+				break;
+			case 12: /* att610 -- Start blinking cursor (IGNORED) */
+				break;
+			case 25:
+				MODBIT(term.c.state, !set, CURSOR_HIDE);
+				break;
+			case 1000: /* 1000,1002: enable xterm mouse report */
+				MODBIT(term.mode, set, MODE_MOUSEBTN);
+				break;
+			case 1002:
+				MODBIT(term.mode, set, MODE_MOUSEMOTION);
+				break;
+			case 1049: /* = 1047 and 1048 */
+			case 47:
+			case 1047:
+				if(IS_SET(MODE_ALTSCREEN))
+					tclearregion(0, 0, term.col-1, term.row-1);
+				if ((set && !IS_SET(MODE_ALTSCREEN)) ||
+				    (!set && IS_SET(MODE_ALTSCREEN))) {
+					    tswapscreen();
+				}
+				if (*args != 1049)
+					break;
+				/* pass through */
+			case 1048:
+				tcursor((set) ? CURSOR_SAVE : CURSOR_LOAD);
+				break;
+			default:
+				fprintf(stderr,
+					"erresc: unknown private set/reset mode %d\n",
+					*args);
+				break;
+			}
+		} else {
+			switch(*args) {
+			case 4:
+				MODBIT(term.mode, set, MODE_INSERT);
+				break;
+			default:
+				fprintf(stderr,
+					"erresc: unknown set/reset mode %d\n",
+					*args);
+				break;
+			}
+		}
+	}
+}
+#undef MODBIT
+
+
 void
 csihandle(void) {
-	switch(escseq.mode) {
+	switch(csiescseq.mode) {
 	default:
 	unknown:
 		fprintf(stderr, "erresc: unknown csi ");
@@ -1174,37 +1261,37 @@ csihandle(void) {
 		/* die(""); */
 		break;
 	case '@': /* ICH -- Insert <n> blank char */
-		DEFAULT(escseq.arg[0], 1);
-		tinsertblank(escseq.arg[0]);
+		DEFAULT(csiescseq.arg[0], 1);
+		tinsertblank(csiescseq.arg[0]);
 		break;
 	case 'A': /* CUU -- Cursor <n> Up */
 	case 'e':
-		DEFAULT(escseq.arg[0], 1);
-		tmoveto(term.c.x, term.c.y-escseq.arg[0]);
+		DEFAULT(csiescseq.arg[0], 1);
+		tmoveto(term.c.x, term.c.y-csiescseq.arg[0]);
 		break;
 	case 'B': /* CUD -- Cursor <n> Down */
-		DEFAULT(escseq.arg[0], 1);
-		tmoveto(term.c.x, term.c.y+escseq.arg[0]);
+		DEFAULT(csiescseq.arg[0], 1);
+		tmoveto(term.c.x, term.c.y+csiescseq.arg[0]);
 		break;
 	case 'C': /* CUF -- Cursor <n> Forward */
 	case 'a':
-		DEFAULT(escseq.arg[0], 1);
-		tmoveto(term.c.x+escseq.arg[0], term.c.y);
+		DEFAULT(csiescseq.arg[0], 1);
+		tmoveto(term.c.x+csiescseq.arg[0], term.c.y);
 		break;
 	case 'D': /* CUB -- Cursor <n> Backward */
-		DEFAULT(escseq.arg[0], 1);
-		tmoveto(term.c.x-escseq.arg[0], term.c.y);
+		DEFAULT(csiescseq.arg[0], 1);
+		tmoveto(term.c.x-csiescseq.arg[0], term.c.y);
 		break;
 	case 'E': /* CNL -- Cursor <n> Down and first col */
-		DEFAULT(escseq.arg[0], 1);
-		tmoveto(0, term.c.y+escseq.arg[0]);
+		DEFAULT(csiescseq.arg[0], 1);
+		tmoveto(0, term.c.y+csiescseq.arg[0]);
 		break;
 	case 'F': /* CPL -- Cursor <n> Up and first col */
-		DEFAULT(escseq.arg[0], 1);
-		tmoveto(0, term.c.y-escseq.arg[0]);
+		DEFAULT(csiescseq.arg[0], 1);
+		tmoveto(0, term.c.y-csiescseq.arg[0]);
 		break;
 	case 'g': /* TBC -- Tabulation clear */
-		switch (escseq.arg[0]) {
+		switch (csiescseq.arg[0]) {
 		case 0: /* clear current tab stop */
 			term.tabs[term.c.x] = 0;
 			break;
@@ -1217,23 +1304,23 @@ csihandle(void) {
 		break;
 	case 'G': /* CHA -- Move to <col> */
 	case '`': /* XXX: HPA -- same? */
-		DEFAULT(escseq.arg[0], 1);
-		tmoveto(escseq.arg[0]-1, term.c.y);
+		DEFAULT(csiescseq.arg[0], 1);
+		tmoveto(csiescseq.arg[0]-1, term.c.y);
 		break;
 	case 'H': /* CUP -- Move to <row> <col> */
 	case 'f': /* XXX: HVP -- same? */
-		DEFAULT(escseq.arg[0], 1);
-		DEFAULT(escseq.arg[1], 1);
-		tmoveto(escseq.arg[1]-1, escseq.arg[0]-1);
+		DEFAULT(csiescseq.arg[0], 1);
+		DEFAULT(csiescseq.arg[1], 1);
+		tmoveto(csiescseq.arg[1]-1, csiescseq.arg[0]-1);
 		break;
 	case 'I': /* CHT -- Cursor Forward Tabulation <n> tab stops */
-		DEFAULT(escseq.arg[0], 1);
-		while (escseq.arg[0]--)
-			tputtab();
+		DEFAULT(csiescseq.arg[0], 1);
+		while (csiescseq.arg[0]--)
+			tputtab(1);
 		break;
 	case 'J': /* ED -- Clear screen */
 		sel.bx = -1;
-		switch(escseq.arg[0]) {
+		switch(csiescseq.arg[0]) {
 		case 0: /* below */
 			tclearregion(term.c.x, term.c.y, term.col-1, term.c.y);
 			if(term.c.y < term.row-1)
@@ -1252,7 +1339,7 @@ csihandle(void) {
 		}
 		break;
 	case 'K': /* EL -- Clear line */
-		switch(escseq.arg[0]) {
+		switch(csiescseq.arg[0]) {
 		case 0: /* right */
 			tclearregion(term.c.x, term.c.y, term.col-1, term.c.y);
 			break;
@@ -1265,152 +1352,54 @@ csihandle(void) {
 		}
 		break;
 	case 'S': /* SU -- Scroll <n> line up */
-		DEFAULT(escseq.arg[0], 1);
-		tscrollup(term.top, escseq.arg[0]);
+		DEFAULT(csiescseq.arg[0], 1);
+		tscrollup(term.top, csiescseq.arg[0]);
 		break;
 	case 'T': /* SD -- Scroll <n> line down */
-		DEFAULT(escseq.arg[0], 1);
-		tscrolldown(term.top, escseq.arg[0]);
+		DEFAULT(csiescseq.arg[0], 1);
+		tscrolldown(term.top, csiescseq.arg[0]);
 		break;
 	case 'L': /* IL -- Insert <n> blank lines */
-		DEFAULT(escseq.arg[0], 1);
-		tinsertblankline(escseq.arg[0]);
+		DEFAULT(csiescseq.arg[0], 1);
+		tinsertblankline(csiescseq.arg[0]);
 		break;
 	case 'l': /* RM -- Reset Mode */
-		if(escseq.priv) {
-			switch(escseq.arg[0]) {
-			case 1:
-				term.mode &= ~MODE_APPKEYPAD;
-				break;
-			case 5: /* DECSCNM -- Remove reverse video */
-				if(IS_SET(MODE_REVERSE)) {
-					term.mode &= ~MODE_REVERSE;
-					draw();
-				}
-				break;
-			case 7:
-				term.mode &= ~MODE_WRAP;
-				break;
-			case 12: /* att610 -- Stop blinking cursor (IGNORED) */
-				break;
-			case 20:
-				term.mode &= ~MODE_CRLF;
-				break;
-			case 25:
-				term.c.state |= CURSOR_HIDE;
-				break;
-			case 1000: /* disable X11 xterm mouse reporting */
-				term.mode &= ~MODE_MOUSEBTN;
-				break;
-			case 1002:
-				term.mode &= ~MODE_MOUSEMOTION;
-				break;
-			case 1049: /* = 1047 and 1048 */
-			case 47:
-			case 1047:
-				if(IS_SET(MODE_ALTSCREEN)) {
-					tclearregion(0, 0, term.col-1, term.row-1);
-					tswapscreen();
-				}
-				if(escseq.arg[0] != 1049)
-					break;
-			case 1048:
-				tcursor(CURSOR_LOAD);
-				break;
-			default:
-				goto unknown;
-			}
-		} else {
-			switch(escseq.arg[0]) {
-			case 4:
-				term.mode &= ~MODE_INSERT;
-				break;
-			default:
-				goto unknown;
-			}
-		}
+		tsetmode(csiescseq.priv, 0, csiescseq.arg, csiescseq.narg);
 		break;
 	case 'M': /* DL -- Delete <n> lines */
-		DEFAULT(escseq.arg[0], 1);
-		tdeleteline(escseq.arg[0]);
+		DEFAULT(csiescseq.arg[0], 1);
+		tdeleteline(csiescseq.arg[0]);
 		break;
 	case 'X': /* ECH -- Erase <n> char */
-		DEFAULT(escseq.arg[0], 1);
-		tclearregion(term.c.x, term.c.y, term.c.x + escseq.arg[0], term.c.y);
+		DEFAULT(csiescseq.arg[0], 1);
+		tclearregion(term.c.x, term.c.y, term.c.x + csiescseq.arg[0], term.c.y);
 		break;
 	case 'P': /* DCH -- Delete <n> char */
-		DEFAULT(escseq.arg[0], 1);
-		tdeletechar(escseq.arg[0]);
+		DEFAULT(csiescseq.arg[0], 1);
+		tdeletechar(csiescseq.arg[0]);
+		break;
+	case 'Z': /* CBT -- Cursor Backward Tabulation <n> tab stops */
+		DEFAULT(csiescseq.arg[0], 1);
+		while (csiescseq.arg[0]--)
+			tputtab(0);
 		break;
-	/* XXX: (CSI n Z) CBT -- Cursor Backward Tabulation <n> tab stops */
 	case 'd': /* VPA -- Move to <row> */
-		DEFAULT(escseq.arg[0], 1);
-		tmoveto(term.c.x, escseq.arg[0]-1);
+		DEFAULT(csiescseq.arg[0], 1);
+		tmoveto(term.c.x, csiescseq.arg[0]-1);
 		break;
 	case 'h': /* SM -- Set terminal mode */
-		if(escseq.priv) {
-			switch(escseq.arg[0]) {
-			case 1:
-				term.mode |= MODE_APPKEYPAD;
-				break;
-			case 5: /* DECSCNM -- Reverve video */
-				if(!IS_SET(MODE_REVERSE)) {
-					term.mode |= MODE_REVERSE;
-					draw();
-				}
-				break;
-			case 7:
-				term.mode |= MODE_WRAP;
-				break;
-			case 20:
-				term.mode |= MODE_CRLF;
-				break;
-			case 12: /* att610 -- Start blinking cursor (IGNORED) */
-				 /* fallthrough for xterm cvvis = CSI [ ? 12 ; 25 h */
-				if(escseq.narg > 1 && escseq.arg[1] != 25)
-					break;
-			case 25:
-				term.c.state &= ~CURSOR_HIDE;
-				break;
-			case 1000: /* 1000,1002: enable xterm mouse report */
-				term.mode |= MODE_MOUSEBTN;
-				break;
-			case 1002:
-				term.mode |= MODE_MOUSEMOTION;
-				break;
-			case 1049: /* = 1047 and 1048 */
-			case 47:
-			case 1047:
-				if(IS_SET(MODE_ALTSCREEN))
-					tclearregion(0, 0, term.col-1, term.row-1);
-				else
-					tswapscreen();
-				if(escseq.arg[0] != 1049)
-					break;
-			case 1048:
-				tcursor(CURSOR_SAVE);
-				break;
-			default: goto unknown;
-			}
-		} else {
-			switch(escseq.arg[0]) {
-			case 4:
-				term.mode |= MODE_INSERT;
-				break;
-			default: goto unknown;
-			}
-		};
+		tsetmode(csiescseq.priv, 1, csiescseq.arg, csiescseq.narg);
 		break;
 	case 'm': /* SGR -- Terminal attribute (color) */
-		tsetattr(escseq.arg, escseq.narg);
+		tsetattr(csiescseq.arg, csiescseq.narg);
 		break;
 	case 'r': /* DECSTBM -- Set Scrolling Region */
-		if(escseq.priv)
+		if(csiescseq.priv)
 			goto unknown;
 		else {
-			DEFAULT(escseq.arg[0], 1);
-			DEFAULT(escseq.arg[1], term.row);
-			tsetscroll(escseq.arg[0]-1, escseq.arg[1]-1);
+			DEFAULT(csiescseq.arg[0], 1);
+			DEFAULT(csiescseq.arg[1], term.row);
+			tsetscroll(csiescseq.arg[0]-1, csiescseq.arg[1]-1);
 			tmoveto(0, 0);
 		}
 		break;
@@ -1427,8 +1416,8 @@ void
 csidump(void) {
 	int i;
 	printf("ESC[");
-	for(i = 0; i < escseq.len; i++) {
-		uint c = escseq.buf[i] & 0xff;
+	for(i = 0; i < csiescseq.len; i++) {
+		uint c = csiescseq.buf[i] & 0xff;
 		if(isprint(c)) putchar(c);
 		else if(c == '\n') printf("(\\n)");
 		else if(c == '\r') printf("(\\r)");
@@ -1440,15 +1429,99 @@ csidump(void) {
 
 void
 csireset(void) {
-	memset(&escseq, 0, sizeof(escseq));
+	memset(&csiescseq, 0, sizeof(csiescseq));
+}
+
+void
+strhandle(void) {
+	char *p;
+
+	/*
+	 * TODO: make this being useful in case of color palette change.
+	 */
+	strparse();
+
+	p = strescseq.buf; 
+
+	switch(strescseq.type) {
+	case ']': /* OSC -- Operating System Command */
+		switch(p[0]) {
+		case '0':
+		case '1':
+		case '2':
+			/*
+			 * TODO: Handle special chars in string, like umlauts.
+			 */
+			if(p[1] == ';') {
+				XStoreName(xw.dpy, xw.win, strescseq.buf+2);
+			}
+			break;
+		case ';':
+			XStoreName(xw.dpy, xw.win, strescseq.buf+1);
+			break;
+		case '4': /* TODO: Set color (arg0) to "rgb:%hexr/$hexg/$hexb" (arg1) */
+			break;
+		default:
+			fprintf(stderr, "erresc: unknown str ");
+			strdump();
+			break;
+		}
+		break;
+	case 'P': /* DSC -- Device Control String */
+	case '_': /* APC -- Application Program Command */
+	case '^': /* PM -- Privacy Message */
+	default:
+		fprintf(stderr, "erresc: unknown str ");
+		strdump();
+		/* die(""); */
+		break;
+	}
 }
 
 void
-tputtab(void) {
-	unsigned x;
+strparse(void) {
+	/*
+	 * TODO: Implement parsing like for CSI when required.
+	 * Format: ESC type cmd ';' arg0 [';' argn] ESC \
+	 */
+	return;
+}
 
-	for (x = term.c.x + 1; x < term.col && !term.tabs[x]; ++x)
-		/* nothing */ ;
+void
+strdump(void) {
+	int i;
+	printf("ESC%c", strescseq.type);
+	for(i = 0; i < strescseq.len; i++) {
+		uint c = strescseq.buf[i] & 0xff;
+		if(isprint(c)) putchar(c);
+		else if(c == '\n') printf("(\\n)");
+		else if(c == '\r') printf("(\\r)");
+		else if(c == 0x1b) printf("(\\e)");
+		else printf("(%02x)", c);
+	}
+	printf("ESC\\\n");
+}
+
+void
+strreset(void) {
+	memset(&strescseq, 0, sizeof(strescseq));
+}
+
+void
+tputtab(bool forward) {
+	unsigned x = term.c.x;
+
+	if (forward) {
+		if (x == term.col)
+			return;
+		for (++x; x < term.col && !term.tabs[x]; ++x)
+			/* nothing */ ;
+	} else {
+		if (x == 0)
+			return;
+		for (--x; x > 0 && !term.tabs[x]; --x)
+			/* nothing */ ;
+	}
 	tmoveto(x, term.c.y);
 }
 
@@ -1457,25 +1530,31 @@ tputc(char *c) {
 	char ascii = *c;
 	if(term.esc & ESC_START) {
 		if(term.esc & ESC_CSI) {
-			escseq.buf[escseq.len++] = ascii;
-			if(BETWEEN(ascii, 0x40, 0x7E) || escseq.len >= ESC_BUF_SIZ) {
+			csiescseq.buf[csiescseq.len++] = ascii;
+			if(BETWEEN(ascii, 0x40, 0x7E) || csiescseq.len >= ESC_BUF_SIZ) {
 				term.esc = 0;
 				csiparse(), csihandle();
 			}
-			/* TODO: handle other OSC */
-		} else if(term.esc & ESC_OSC) {
-			if(ascii == ';') {
-				term.titlelen = 0;
-				term.esc = ESC_START | ESC_TITLE;
-			}
-		} else if(term.esc & ESC_TITLE) {
-			if(ascii == '\a' || term.titlelen+1 >= ESC_TITLE_SIZ) {
+		} else if(term.esc & ESC_STR) {
+			switch(ascii) {
+			case '\033':
+				term.esc = ESC_START | ESC_STR_END;
+				break;
+			case '\a': /* backwards compatibility to xterm */
 				term.esc = 0;
-				term.title[term.titlelen] = '\0';
-				XStoreName(xw.dpy, xw.win, term.title);
-			} else {
-				term.title[term.titlelen++] = ascii;
+				strhandle();
+				break;
+			default:
+				strescseq.buf[strescseq.len++] = ascii;
+				if (strescseq.len+1 >= STR_BUF_SIZ) {
+					term.esc = 0;
+					strhandle();
+				}
 			}
+		} else if(term.esc & ESC_STR_END) {
+			term.esc = 0;
+			if(ascii == '\\')
+				strhandle();
 		} else if(term.esc & ESC_ALTCHARSET) {
 			switch(ascii) {
 			case '0': /* Line drawing crap */
@@ -1493,8 +1572,13 @@ tputc(char *c) {
 			case '[':
 				term.esc |= ESC_CSI;
 				break;
-			case ']':
-				term.esc |= ESC_OSC;
+			case 'P': /* DCS -- Device Control String */
+			case '_': /* APC -- Application Program Command */
+			case '^': /* PM -- Privacy Message */
+			case ']': /* OSC -- Operating System Command */
+				strreset();
+				strescseq.type = ascii;
+				term.esc |= ESC_STR;
 				break;
 			case '(':
 				term.esc |= ESC_ALTCHARSET;
@@ -1541,6 +1625,9 @@ tputc(char *c) {
 				tcursor(CURSOR_LOAD);
 				term.esc = 0;
 				break;
+			case '\\': /* ST -- Stop */
+				term.esc = 0;
+				break;
 			default:
 				fprintf(stderr, "erresc: unknown sequence ESC 0x%02X '%c'\n",
 				    (uchar) ascii, isprint(ascii)?ascii:'.');
@@ -1552,7 +1639,7 @@ tputc(char *c) {
 			sel.bx = -1;
 		switch(ascii) {
 		case '\t':
-			tputtab();
+			tputtab(1);
 			break;
 		case '\b':
 			tmoveto(term.c.x-1, term.c.y);
@@ -1708,8 +1795,8 @@ void
 xclear(int x1, int y1, int x2, int y2) {
 	XSetForeground(xw.dpy, dc.gc, dc.col[IS_SET(MODE_REVERSE) ? DefaultFG : DefaultBG]);
 	XFillRectangle(xw.dpy, xw.buf, dc.gc,
-	               BORDER + x1 * xw.cw, BORDER + y1 * xw.ch,
-	               (x2-x1+1) * xw.cw, (y2-y1+1) * xw.ch);
+		       BORDER + x1 * xw.cw, BORDER + y1 * xw.ch,
+		       (x2-x1+1) * xw.cw, (y2-y1+1) * xw.ch);
 }
 
 void
@@ -1884,8 +1971,8 @@ xdraws(char *s, Glyph base, int x, int y, int charlen, int bytelen) {
 /* copy buffer pixmap to screen pixmap */
 void
 xcopy() {
-        XdbeSwapInfo swpinfo[1] = {{xw.win, XdbeCopied}};
-        XdbeSwapBuffers(xw.dpy, swpinfo, 1);
+	XdbeSwapInfo swpinfo[1] = {{xw.win, XdbeCopied}};
+	XdbeSwapBuffers(xw.dpy, swpinfo, 1);
 
 }
 
@@ -1982,8 +2069,8 @@ expose(XEvent *ev) {
 	if(xw.state & WIN_REDRAW) {
 		if(!e->count)
 			xw.state &= ~WIN_REDRAW;
-        }
-        xcopy();
+	}
+	xcopy();
 }
 
 void