aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--LICENSE43
-rw-r--r--Makefile69
-rw-r--r--README28
-rw-r--r--TODO9
-rw-r--r--config.mk31
-rw-r--r--st.c926
-rw-r--r--st.h181
-rw-r--r--st.info54
-rw-r--r--std.c363
9 files changed, 1278 insertions, 426 deletions
diff --git a/LICENSE b/LICENSE
index ce8ec88..ee32d28 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,22 +1,25 @@
1MIT/X Consortium License 1Copyright (c) 2009, Aurélien APTEL <aurelien dot aptel at gmail dot com>
2Copyright (c) 2009, Anselm R Garbe <garbeam at gmail dot com>
2 3
3© 2007-2008 Anselm R Garbe <garbeam at gmail dot com> 4Redistribution and use in source and binary forms, with or without
4© 2008 Matthias Christian Ott <ott at enolink dot de> 5modification, are permitted provided that the following conditions are met:
6 * Redistributions of source code must retain the above copyright
7 notice, this list of conditions and the following disclaimer.
8 * Redistributions in binary form must reproduce the above copyright
9 notice, this list of conditions and the following disclaimer in the
10 documentation and/or other materials provided with the distribution.
11 * Neither the name of the copyright holder nor the names of its
12 contributors may be used to endorse or promote products derived
13 from this software without specific prior written permission.
5 14
6Permission is hereby granted, free of charge, to any person obtaining a 15THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
7copy of this software and associated documentation files (the "Software"), 16"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
8to deal in the Software without restriction, including without limitation 17LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
9the rights to use, copy, modify, merge, publish, distribute, sublicense, 18A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
10and/or sell copies of the Software, and to permit persons to whom the 19HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
11Software is furnished to do so, subject to the following conditions: 20SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
12 21LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
13The above copyright notice and this permission notice shall be included in 22DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
14all copies or substantial portions of the Software. 23THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
15 24(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 25OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22DEALINGS IN THE SOFTWARE.
diff --git a/Makefile b/Makefile
index 0f74456..b0bd42d 100644
--- a/Makefile
+++ b/Makefile
@@ -1,45 +1,50 @@
1# st - simple terminal 1# st - simple terminal
2# See LICENSE file for copyright and license details. 2# See LICENSE file for copyright and license details.
3 3
4VERSION = 0.0 4include config.mk
5 5
6PREFIX = /usr/local 6SRC = st.c
7MANDIR = $(PREFIX)/share/man 7OBJ = ${SRC:.c=.o}
8 8
9CFLAGS = -DVERSION=\"0.0\" -D_GNU_SOURCE 9all: options st
10 10
11all: st std 11options:
12 @echo st build options:
13 @echo "CFLAGS = ${CFLAGS}"
14 @echo "LDFLAGS = ${LDFLAGS}"
15 @echo "CC = ${CC}"
16
17.c.o:
18 @echo CC $<
19 @${CC} -c ${CFLAGS} $<
20
21${OBJ}: config.mk
22
23st: ${OBJ}
24 @echo CC -o $@
25 @${CC} -o $@ ${OBJ} ${LDFLAGS}
12 26
13clean: 27clean:
14 rm -f st std 28 @echo cleaning
15 rm -f st.o std.o 29 @rm -f st ${OBJ} st-${VERSION}.tar.gz
16 rm -f st-$(VERSION).tar.gz
17 30
18dist: clean 31dist: clean
19 mkdir st-$(VERSION) 32 @echo creating dist tarball
20 cp -f LICENSE README st-$(VERSION) 33 @mkdir -p st-${VERSION}
21 cp -f Makefile config.mk st-$(VERSION) 34 @cp -R LICENSE Makefile README config.mk st.h ${SRC} st-${VERSION}
22 cp -f st.1 std.1 st-$(VERSION) 35 @tar -cf st-${VERSION}.tar st-${VERSION}
23 cp -f st.c std.c st-$(VERSION) 36 @gzip st-${VERSION}.tar
24 tar -czf st-$(VERSION).tar st-$(VERSION) 37 @rm -rf st-${VERSION}
25 rm -rf st-$(VERSION) 38
26 39install: all
27install: 40 @echo installing executable file to ${DESTDIR}${PREFIX}/bin
28 mkdir -p $(DESTDIR)$(PREFIX)/bin 41 @mkdir -p ${DESTDIR}${PREFIX}/bin
29 cp -f st $(DESTDIR)$(PREFIX)/bin 42 @cp -f st ${DESTDIR}${PREFIX}/bin
30 cp -f std $(DESTDIR)$(PREFIX)/bin 43 @chmod 755 ${DESTDIR}${PREFIX}/bin/st
31 chmod 755 $(DESTDIR)$(PREFIX)/bin/st 44 @tic st.info
32 chmod 755 $(DESTDIR)$(PREFIX)/bin/std
33 mkdir -p $(DESTDIR)$(MANDIR)/man1
34 sed 's/VERSION/$(VERSION)/g' < st.1 > $(DESTDIR)$(MANDIR)/man1/st.1
35 chmod 644 $(DESTDIR)$(MANDIR)/man1/st.1
36 sed 's/VERSION/$(VERSION)/g' < std.1 > $(DESTDIR)$(MANDIR)/man1/std.1
37 chmod 644 $(DESTDIR)$(MANDIR)/man1/std.1
38 45
39uninstall: 46uninstall:
40 rm -f $(DESTDIR)$(PREFIX)/bin/st 47 @echo removing executable file from ${DESTDIR}${PREFIX}/bin
41 rm -f $(DESTDIR)$(PREFIX)/bin/std 48 @rm -f ${DESTDIR}${PREFIX}/bin/st
42 rm -f $(DESTDIR)$(MANDIR)/man1/st.1
43 rm -f $(DESTDIR)$(MANDIR)/man1/std.1
44 49
45.PHONY: all clean dist install uninstall 50.PHONY: all options clean dist install uninstall
diff --git a/README b/README
new file mode 100644
index 0000000..09a7fff
--- /dev/null
+++ b/README
@@ -0,0 +1,28 @@
1st - simple terminal
2--------------------
3st is a simple virtual terminal emulator for X which sucks less.
4
5
6Requirements
7------------
8In order to build st you need the Xlib header files.
9
10
11Installation
12------------
13Edit config.mk to match your local setup (st is installed into
14the /usr/local namespace by default).
15
16Afterwards enter the following command to build and install st (if
17necessary as root):
18
19 make clean install
20
21
22Running st
23----------
24See the man page for details.
25
26Credits
27-------
28Based on Aurélien APTEL <aurelien dot aptel at gmail dot com> bt source code.
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..70fa785
--- /dev/null
+++ b/TODO
@@ -0,0 +1,9 @@
1 - write a clean terminfo entry
2 - write global "setup" func
3 - try to split more logic/gfx
4 - optimize drawing
5 - handle copy/paste
6 - fix fork/child exit problem
7 - fix resize (shrinking should move last line up)
8 - handle utf8
9 - refactor/clean code
diff --git a/config.mk b/config.mk
new file mode 100644
index 0000000..62eb48f
--- /dev/null
+++ b/config.mk
@@ -0,0 +1,31 @@
1# st version
2VERSION = 0.0
3
4# Customize below to fit your system
5
6# paths
7PREFIX = /usr/local
8MANPREFIX = ${PREFIX}/share/man
9
10X11INC = /usr/X11R6/include
11X11LIB = /usr/X11R6/lib
12
13# Xinerama, comment if you don't want it
14#XINERAMALIBS = -L${X11LIB} -lXinerama
15#XINERAMAFLAGS = -DXINERAMA
16
17# includes and libs
18INCS = -I. -I/usr/include -I${X11INC}
19LIBS = -L/usr/lib -lc -L${X11LIB} -lX11 ${XINERAMALIBS}
20
21# flags
22CPPFLAGS = -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS}
23CFLAGS = -std=c99 -pedantic -Wall -Os ${INCS} ${CPPFLAGS}
24LDFLAGS = -s ${LIBS}
25
26# Solaris
27#CFLAGS = -fast ${INCS} -DVERSION=\"${VERSION}\"
28#LDFLAGS = ${LIBS}
29
30# compiler and linker
31CC = cc
diff --git a/st.c b/st.c
index 8a8804f..d5f004b 100644
--- a/st.c
+++ b/st.c
@@ -1,17 +1,921 @@
1/* See LICENSE file for copyright and license details. */ 1/* See LICENSE for licence details. */
2#include <stdio.h> 2#include "st.h"
3#include <stdlib.h> 3
4#include <string.h> 4/* Globals */
5DC dc;
6XWindow xw;
7Term term;
8Escseq escseq;
9int cmdfd;
10int running;
11
12void
13die(const char *errstr, ...) {
14 va_list ap;
15
16 va_start(ap, errstr);
17 vfprintf(stderr, errstr, ap);
18 va_end(ap);
19 exit(EXIT_FAILURE);
20}
21
22void
23execsh(void) {
24 char *args[3] = {SHELL, "-i", NULL};
25 putenv("TERM=" TNAME);
26 execvp(SHELL, args);
27}
28
29void
30xbell(void) { /* visual bell */
31 XRectangle r = { 0, 0, xw.w, xw.h };
32 XSetForeground(xw.dis, dc.gc, dc.col[BellCol]);
33 XFillRectangles(xw.dis, xw.win, dc.gc, &r, 1);
34 XFlush(xw.dis);
35 usleep(30000);
36 draw(SCredraw);
37}
38
39void
40ttynew(void) {
41 int m, s;
42 pid_t pid;
43 char *pts;
44
45 if((m = posix_openpt(O_RDWR | O_NOCTTY)) < 0)
46 die("openpt");
47 if(grantpt(m) == -1)
48 die("grandpt");
49 if(unlockpt(m) == -1)
50 die("unlockpt");
51 if((pts = ptsname(m)) == NULL)
52 die("ptsname");
53 if((s = open(pts, O_RDWR | O_NOCTTY)) < 0)
54 die("slave open");
55 fcntl(s, F_SETFL, O_NDELAY);
56 switch(pid = fork()) {
57 case -1:
58 die("fork");
59 break;
60 case 0:
61 setsid(); /* create a new process group */
62 dup2(s, STDIN_FILENO);
63 dup2(s, STDOUT_FILENO);
64 dup2(s, STDERR_FILENO);
65 if(ioctl(s, TIOCSCTTY, NULL) < 0)
66 die("slave TTIOCSTTY");
67 execsh();
68 break;
69 default:
70 close(s);
71 cmdfd = m;
72 }
73}
74
75void
76dump(char c) {
77 static int col;
78 fprintf(stderr, " %02x %c ", c, isprint(c)?c:'.');
79 if(++col % 10 == 0)
80 fprintf(stderr, "\n");
81}
82
83void
84ttyread(void) {
85 char buf[BUFSIZ] = {0};
86 int ret;
87
88 switch(ret = read(cmdfd, buf, BUFSIZ)) {
89 case -1: /* error or exit */
90 /* XXX: be more precise */
91 running = 0;
92 break;
93 default:
94 tputs(buf, ret);
95 }
96}
97
98void
99ttywrite(char *s, size_t n) {
100 if(write(cmdfd, s, n) == -1)
101 die("write error on tty.");
102}
103
104void
105ttyresize(int x, int y) {
106 struct winsize w;
107
108 w.ws_row = term.row;
109 w.ws_col = term.col;
110 w.ws_xpixel = w.ws_ypixel = 0;
111 if(ioctl(cmdfd, TIOCSWINSZ, &w) < 0)
112 fprintf(stderr, "Couldn't set window size: %m\n");
113}
5 114
6int 115int
7main(int argc, char *argv[]) { 116escfinal(char c) {
8 if(argc == 2 && !strcmp("-v", argv[1])) { 117 if(escseq.len == 1)
9 fprintf(stderr, "st-"VERSION", © 2007-2008 st engineers, see LICENSE for details\n"); 118 switch(c) {
10 exit(EXIT_SUCCESS); 119 case '[':
120 case ']':
121 case '(':
122 return 0;
123 case '=':
124 case '>':
125 default:
126 return 1;
127 }
128 else if(BETWEEN(c, 0x40, 0x7E))
129 return 1;
130 return 0;
131}
132
133void
134tcpos(int mode) {
135 static int x = 0;
136 static int y = 0;
137
138 if(mode == CSsave)
139 x = term.c.x, y = term.c.y;
140 else if(mode == CSload)
141 tmoveto(x, y);
142}
143
144void
145tnew(int col, int row) { /* screen size */
146 term.row = row, term.col = col;
147 term.top = 0, term.bot = term.row - 1;
148 /* mode */
149 term.mode = TMwrap;
150 /* cursor */
151 term.c.attr.mode = ATnone;
152 term.c.attr.fg = DefaultFG;
153 term.c.attr.bg = DefaultBG;
154 term.c.x = term.c.y = 0;
155 term.c.hidden = 0;
156 /* allocate screen */
157 term.line = calloc(term.row, sizeof(Line));
158 for(row = 0 ; row < term.row; row++)
159 term.line[row] = calloc(term.col, sizeof(Glyph));
160}
161
162void
163tscroll(void) {
164 Line temp = term.line[term.top];
165 int i;
166
167 for(i = term.top; i < term.bot; i++)
168 term.line[i] = term.line[i+1];
169 memset(temp, 0, sizeof(Glyph) * term.col);
170 term.line[term.bot] = temp;
171 xscroll();
172}
173
174void
175tnewline(void) {
176 int y = term.c.y + 1;
177
178 if(y > term.bot) {
179 tscroll(), y = term.bot;
11 } 180 }
12 else if(argc != 1) { 181 tmoveto(0, y);
13 fprintf(stderr, "usage: st [-v]\n"); 182}
14 exit(EXIT_FAILURE); 183
184int
185escaddc(char c) {
186 escseq.buf[escseq.len++] = c;
187 if(escfinal(c) || escseq.len >= ESCSIZ) {
188 escparse(), eschandle();
189 return 0;
15 } 190 }
191 return 1;
192}
193
194void
195escparse(void) {
196 /* int noarg = 1; */
197 char *p = escseq.buf;
198
199 escseq.narg = 0;
200 switch(escseq.pre = *p++) {
201 case '[': /* CSI */
202 if(*p == '?')
203 escseq.priv = 1, p++;
204
205 while(p < escseq.buf+escseq.len) {
206 while(isdigit(*p)) {
207 escseq.arg[escseq.narg] *= 10;
208 escseq.arg[escseq.narg] += *(p++) - '0'/*, noarg = 0 */;
209 }
210 if(*p == ';')
211 escseq.narg++, p++;
212 else {
213 escseq.mode = *p;
214 escseq.narg++;
215 return;
216 }
217 }
218 break;
219 case '(':
220 /* humf charset stuff */
221 break;
222 }
223}
224
225void
226tmoveto(int x, int y) {
227 term.c.x = x < 0 ? 0 : x >= term.col ? term.col-1 : x;
228 term.c.y = y < 0 ? 0 : y >= term.row ? term.row-1 : y;
229}
230
231void
232tcursor(int dir) {
233 int xi = term.c.x, yi = term.c.y;
234 int xf = xi, yf = yi;
235
236 switch(dir) {
237 case CSup:
238 yf--;
239 break;
240 case CSdown:
241 yf++;
242 break;
243 case CSleft:
244 xf--;
245 if(xf < 0) {
246 xf = term.col-1, yf--;
247 if(yf < term.top)
248 yf = term.top, xf = 0;
249 }
250 break;
251 case CSright:
252 xf++;
253 if(xf >= term.col) {
254 xf = 0, yf++;
255 if(yf > term.bot)
256 yf = term.bot, tscroll();
257 }
258 break;
259 }
260 tmoveto(xf, yf);
261}
262
263void
264tsetchar(char c) {
265 term.line[term.c.y][term.c.x] = term.c.attr;
266 term.line[term.c.y][term.c.x].c = c;
267 term.line[term.c.y][term.c.x].state |= CRset | CRupdate;
268}
269
270void
271tclearregion(int x1, int y1, int x2, int y2) {
272 int x, y;
273
274 LIMIT(x1, 0, term.col-1);
275 LIMIT(x2, 0, term.col-1);
276 LIMIT(y1, 0, term.row-1);
277 LIMIT(y2, 0, term.row-1);
278
279 /* XXX: could be optimized */
280 for(x = x1; x <= x2; x++)
281 for(y = y1; y <= y2; y++)
282 memset(&term.line[y][x], 0, sizeof(Glyph));
283
284 xclear(x1, y1, x2, y2);
285}
286
287void
288tdeletechar(int n) {
289 int src = term.c.x + n;
290 int dst = term.c.x;
291 int size = term.col - src;
292
293 if(src >= term.col) {
294 tclearregion(term.c.x, term.c.y, term.col-1, term.c.y);
295 return;
296 }
297 memmove(&term.line[term.c.y][dst], &term.line[term.c.y][src], size * sizeof(Glyph));
298 tclearregion(term.col-size, term.c.y, term.col-1, term.c.y);
299}
300
301void
302tinsertblank(int n) {
303 int src = term.c.x;
304 int dst = src + n;
305 int size = term.col - n - src;
306
307 if(dst >= term.col) {
308 tclearregion(term.c.x, term.c.y, term.col-1, term.c.y);
309 return;
310 }
311 memmove(&term.line[term.c.y][dst], &term.line[term.c.y][src], size * sizeof(Glyph));
312 tclearregion(src, term.c.y, dst, term.c.y);
313}
314
315void
316tinsertblankline (int n) {
317 int i;
318 Line blank;
319 int bot = term.bot;
320
321 if(term.c.y > term.bot)
322 bot = term.row - 1;
323 else if(term.c.y < term.top)
324 bot = term.top - 1;
325 if(term.c.y + n >= bot) {
326 tclearregion(0, term.c.y, term.col-1, bot);
327 return;
328 }
329 for(i = bot; i >= term.c.y+n; i--) {
330 /* swap deleted line <-> blanked line */
331 blank = term.line[i];
332 term.line[i] = term.line[i-n];
333 term.line[i-n] = blank;
334 /* blank it */
335 memset(blank, 0, term.col * sizeof(Glyph));
336 }
337}
338
339
340void
341tdeleteline(int n) {
342 int i;
343 Line blank;
344 int bot = term.bot;
345
346 if(term.c.y > term.bot)
347 bot = term.row - 1;
348 else if(term.c.y < term.top)
349 bot = term.top - 1;
350 if(term.c.y + n >= bot) {
351 tclearregion(0, term.c.y, term.col-1, bot);
352 return;
353 }
354 for(i = term.c.y; i <= bot-n; i++) {
355 /* swap deleted line <-> blanked line */
356 blank = term.line[i];
357 term.line[i] = term.line[i+n];
358 term.line[i+n] = blank;
359 /* blank it */
360 memset(blank, 0, term.col * sizeof(Glyph));
361 }
362}
363
364void
365tsetattr(int *attr, int l) {
366 int i;
367
368#ifdef TRUECOLOR /* ESC [ ? <fg/bg> ; <r> ; <g> ; <b> m */
369 Color col;
370 if(escseq.priv && escseq.len == 4) { /* True color extension :) */
371 col = (escseq.arg[1]<<16) + (escseq.arg[2]<<8) + escseq.arg[3];
372 switch(escseq.arg[0]) {
373 case 3: /* foreground */
374 term.c.attr.fg = col;
375 break;
376 case 4: /* background */
377 term.c.attr.bg = col;
378 break;
379 }
380 }
381 else
382#endif
383 for(i = 0; i < l; i++) {
384 switch(attr[i]) {
385 case 0:
386 memset(&term.c.attr, 0, sizeof(term.c.attr));
387 term.c.attr.fg = DefaultFG;
388 term.c.attr.bg = DefaultBG;
389 break;
390 case 1:
391 term.c.attr.mode |= ATbold;
392 break;
393 case 4:
394 term.c.attr.mode |= ATunderline;
395 break;
396 case 7:
397 term.c.attr.mode |= ATreverse;
398 break;
399 case 8:
400 term.c.hidden = CShide;
401 break;
402 case 22:
403 term.c.attr.mode &= ~ATbold;
404 break;
405 case 24:
406 term.c.attr.mode &= ~ATunderline;
407 break;
408 case 27:
409 term.c.attr.mode &= ~ATreverse;
410 break;
411 case 39:
412 term.c.attr.fg = DefaultFG;
413 break;
414 case 49:
415 term.c.attr.fg = DefaultBG;
416 break;
417 default:
418 if(BETWEEN(attr[i], 30, 37))
419 term.c.attr.fg = attr[i] - 30;
420 else if(BETWEEN(attr[i], 40, 47))
421 term.c.attr.bg = attr[i] - 40;
422 break;
423 }
424 }
425}
426
427void
428tsetscroll(int t, int b) {
429 int temp;
430
431 LIMIT(t, 0, term.row-1);
432 LIMIT(b, 0, term.row-1);
433 if(t > b) {
434 temp = t;
435 t = b;
436 b = temp;
437 }
438 term.top = t;
439 term.bot = b;
440}
441
442
443void
444eschandle(void) {
445 /* escdump(); */
446 switch(escseq.pre) {
447 case '[':
448 switch(escseq.mode) {
449 case '@': /* Insert <n> blank char */
450 DEFAULT(escseq.arg[0], 1);
451 tinsertblank(escseq.arg[0]);
452 break;
453 case 'A': /* Cursor <n> Up */
454 case 'e':
455 DEFAULT(escseq.arg[0], 1);
456 tmoveto(term.c.x, term.c.y-escseq.arg[0]);
457 break;
458 case 'B': /* Cursor <n> Down */
459 DEFAULT(escseq.arg[0], 1);
460 tmoveto(term.c.x, term.c.y+escseq.arg[0]);
461 break;
462 case 'C': /* Cursor <n> Forward */
463 case 'a':
464 DEFAULT(escseq.arg[0], 1);
465 tmoveto(term.c.x+escseq.arg[0], term.c.y);
466 break;
467 case 'D': /* Cursor <n> Backward */
468 DEFAULT(escseq.arg[0], 1);
469 tmoveto(term.c.x-escseq.arg[0], term.c.y);
470 break;
471 case 'E': /* Cursor <n> Down and first col */
472 DEFAULT(escseq.arg[0], 1);
473 tmoveto(0, term.c.y+escseq.arg[0]);
474 break;
475 case 'F': /* Cursor <n> Up and first col */
476 DEFAULT(escseq.arg[0], 1);
477 tmoveto(0, term.c.y-escseq.arg[0]);
478 break;
479 case 'G': /* Move to <col> */
480 case '`':
481 DEFAULT(escseq.arg[0], 1);
482 tmoveto(escseq.arg[0]-1, term.c.y);
483 break;
484 case 'H': /* Move to <row> <col> */
485 case 'f':
486 DEFAULT(escseq.arg[0], 1);
487 DEFAULT(escseq.arg[1], 1);
488 tmoveto(escseq.arg[1]-1, escseq.arg[0]-1);
489 break;
490 case 'J': /* Clear screen */
491 switch(escseq.arg[0]) {
492 case 0: /* below */
493 tclearregion(term.c.x, term.c.y, term.col-1, term.row-1);
494 break;
495 case 1: /* above */
496 tclearregion(0, 0, term.c.x, term.c.y);
497 break;
498 case 2: /* all */
499 tclearregion(0, 0, term.col-1, term.row-1);
500 break;
501 }
502 break;
503 case 'K': /* Clear line */
504 switch(escseq.arg[0]) {
505 case 0: /* right */
506 tclearregion(term.c.x, term.c.y, term.col-1, term.c.y);
507 break;
508 case 1: /* left */
509 tclearregion(0, term.c.y, term.c.x, term.c.y);
510 break;
511 case 2: /* all */
512 tclearregion(0, term.c.y, term.col-1, term.c.y);
513 break;
514 }
515 break;
516 case 'L': /* Insert <n> blank lines */
517 DEFAULT(escseq.arg[0], 1);
518 tinsertblankline(escseq.arg[0]);
519 break;
520 case 'M': /* Delete <n> lines */
521 DEFAULT(escseq.arg[0], 1);
522 tdeleteline(escseq.arg[0]);
523 break;
524 case 'P': /* Delete <n> char */
525 DEFAULT(escseq.arg[0], 1);
526 tdeletechar(escseq.arg[0]);
527 break;
528 case 'd': /* Move to <row> */
529 DEFAULT(escseq.arg[0], 1);
530 tmoveto(term.c.x, escseq.arg[0]-1);
531 break;
532 case 'h': /* Set terminal mode */
533 break;
534 case 'm': /* Terminal attribute (color) */
535 tsetattr(escseq.arg, escseq.narg);
536 break;
537 case 'r':
538 if(escseq.priv)
539 ;
540 else {
541 DEFAULT(escseq.arg[0], 1);
542 DEFAULT(escseq.arg[1], term.row);
543 tsetscroll(escseq.arg[0]-1, escseq.arg[1]-1);
544 }
545 break;
546 case 's': /* Save cursor position */
547 tcpos(CSsave);
548 break;
549 case 'u': /* Load cursor position */
550 tcpos(CSload);
551 break;
552 }
553 break;
554 }
555}
556
557void
558escdump(void) {
559 int i;
560 puts("------");
561 printf("rawbuf : %s\n", escseq.buf);
562 printf("prechar : %c\n", escseq.pre);
563 printf("private : %c\n", escseq.priv ? '?' : ' ');
564 printf("narg : %d\n", escseq.narg);
565 if(escseq.narg) {
566 for(i = 0; i < escseq.narg; i++)
567 printf("\targ %d = %d\n", i, escseq.arg[i]);
568 }
569 printf("mode : %c\n", escseq.mode);
570}
571
572void
573escreset(void) {
574 memset(&escseq, 0, sizeof(escseq));
575}
576
577void
578tputc(char c) {
579 static int inesc = 0;
580
581 dump(c);
582 /* start of escseq */
583 if(c == '\033')
584 escreset(), inesc = 1;
585 else if(inesc) {
586 inesc = escaddc(c);
587 } /* normal char */
588 else switch(c) {
589 default:
590 tsetchar(c);
591 tcursor(CSright);
592 break;
593 case '\b':
594 tcursor(CSleft);
595 break;
596 case '\r':
597 tmoveto(0, term.c.y);
598 break;
599 case '\n':
600 tnewline();
601 break;
602 case '\a':
603 xbell();
604 break;
605 }
606}
607
608void
609tputs(char *s, int len) {
610 for(; len > 0; len--)
611 tputc(*s++);
612}
613
614void
615tdump(void) {
616 int row, col;
617 Glyph c;
618
619 for(row = 0; row < term.row; row++) {
620 for(col = 0; col < term.col; col++) {
621 if(col == term.c.x && row == term.c.y)
622 putchar('#');
623 else {
624 c = term.line[row][col];
625 putchar(c.state & CRset ? c.c : '.');
626 }
627 }
628 putchar('\n');
629 }
630}
631
632void
633tresize(int col, int row) {
634 int i;
635 Line *line;
636 int minrow = MIN(row, term.row);
637 int mincol = MIN(col, term.col);
638
639 if(col < 1 || row < 1)
640 return;
641 line = calloc(row, sizeof(Line));
642 for(i = 0 ; i < row; i++)
643 line[i] = calloc(col, sizeof(Glyph));
644 for(i = 0 ; i < minrow; i++) {
645 memcpy(line[i], term.line[i], mincol * sizeof(Glyph));
646 free(term.line[i]);
647 }
648 free(term.line);
649 LIMIT(term.c.x, 0, col-1);
650 LIMIT(term.c.y, 0, row-1);
651 LIMIT(term.top, 0, row-1);
652 LIMIT(term.bot, 0, row-1);
653 // if(term.bot == term.row-1)
654 term.bot = row-1;
655 term.line = line;
656 term.col = col, term.row = row;
657}
658
659unsigned long
660xgetcol(const char *s) {
661 XColor color;
662 Colormap cmap = DefaultColormap(xw.dis, xw.scr);
663
664 if(!XAllocNamedColor(xw.dis, cmap, s, &color, &color)) {
665 color.pixel = WhitePixel(xw.dis, xw.scr);
666 fprintf(stderr, "Could not allocate color '%s'\n", s);
667 }
668 return color.pixel;
669}
670
671
672void
673xclear(int x1, int y1, int x2, int y2) {
674 XClearArea(xw.dis, xw.win,
675 x1 * xw.cw, y1 * xw.ch,
676 (x2-x1+1) * xw.cw, (y2-y1+1) * xw.ch,
677 False);
678}
679
680
681void
682xscroll(void) {
683 int srcy = (term.top+1) * xw.ch;
684 int dsty = term.top * xw.ch;
685 int height = (term.bot-term.top) * xw.ch;
686
687 xcursor(CShide);
688 XCopyArea(xw.dis, xw.win, xw.win, dc.gc, 0, srcy, xw.w, height, 0, dsty);
689 xclear(0, term.bot, term.col-1, term.bot);
690}
691
692
693
694
695void
696xinit(void) {
697 XGCValues values;
698 unsigned long valuemask;
699 XClassHint chint;
700 XWMHints wmhint;
701 XSizeHints shint;
702 char *args[] = {NULL};
703 int i;
704
705 xw.dis = XOpenDisplay(NULL);
706 xw.scr = XDefaultScreen(xw.dis);
707 /* font */
708 dc.font = XLoadQueryFont(xw.dis, FONT);
709 xw.cw = dc.font->max_bounds.rbearing - dc.font->min_bounds.lbearing;
710 xw.ch = dc.font->ascent + dc.font->descent + LINESPACE;
711 /* colors */
712 for(i = 0; i < LEN(colorname); i++)
713 dc.col[i] = xgetcol(colorname[i]);
714 term.c.attr.fg = DefaultFG;
715 term.c.attr.bg = DefaultBG;
716 term.c.attr.mode = ATnone;
717 /* windows */
718 xw.h = term.row * xw.ch;
719 xw.w = term.col * xw.cw;
720 /* XXX: this BORDER is useless after the first resize, handle it in xdraws() */
721 xw.win = XCreateSimpleWindow(xw.dis, XRootWindow(xw.dis, xw.scr), 0, 0,
722 xw.w, xw.h, BORDER,
723 dc.col[DefaultBG],
724 dc.col[DefaultBG]);
725 /* gc */
726 values.foreground = XWhitePixel(xw.dis, xw.scr);
727 values.font = dc.font->fid;
728 valuemask = GCForeground | GCFont;
729 dc.gc = XCreateGC(xw.dis, xw.win, valuemask, &values);
730 XMapWindow(xw.dis, xw.win);
731 /* wm stuff */
732 chint.res_name = TNAME, chint.res_class = TNAME;
733 wmhint.input = 1, wmhint.flags = InputHint;
734 shint.height_inc = xw.ch, shint.width_inc = xw.cw;
735 shint.height = xw.h, shint.width = xw.w;
736 shint.flags = PSize | PResizeInc;
737 XSetWMProperties(xw.dis, xw.win, NULL, NULL, &args[0], 0, &shint, &wmhint, &chint);
738 XStoreName(xw.dis, xw.win, TNAME);
739 XSync(xw.dis, 0);
740}
741
742void
743xdrawc(int x, int y, Glyph g) {
744 XRectangle r = { x * xw.cw, y * xw.ch, xw.cw, xw.ch };
745 unsigned long xfg, xbg;
746
747 /* reverse video */
748 if(g.mode & ATreverse)
749 xfg = dc.col[g.bg], xbg = dc.col[g.fg];
750 else
751 xfg = dc.col[g.fg], xbg = dc.col[g.bg];
752 /* background */
753 XSetForeground(xw.dis, dc.gc, xbg);
754 XFillRectangles(xw.dis, xw.win, dc.gc, &r, 1);
755 /* string */
756 XSetForeground(xw.dis, dc.gc, xfg);
757 XDrawString(xw.dis, xw.win, dc.gc, r.x, r.y+dc.font->ascent, &(g.c), 1);
758 if(g.mode & ATbold) /* XXX: bold hack (draw again at x+1) */
759 XDrawString(xw.dis, xw.win, dc.gc, r.x+1, r.y+dc.font->ascent, &(g.c), 1);
760 /* underline */
761 if(g.mode & ATunderline) {
762 r.y += dc.font->ascent + 1;
763 XDrawLine(xw.dis, xw.win, dc.gc, r.x, r.y, r.x+r.width-1, r.y);
764 }
765}
766
767void
768xcursor(int mode) {
769 static int oldx = 0;
770 static int oldy = 0;
771 Glyph g = {' ', ATnone, DefaultBG, DefaultCS, 0};
772
773 if(term.line[term.c.y][term.c.x].state & CRset)
774 g.c = term.line[term.c.y][term.c.x].c;
775 /* remove the old cursor */
776 if(term.line[oldy][oldx].state & CRset)
777 xdrawc(oldx, oldy, term.line[oldy][oldx]);
778 else xclear(oldx, oldy, oldx, oldy); /* XXX: maybe a bug */
779 if(mode == CSdraw && !term.c.hidden) {
780 xdrawc(term.c.x, term.c.y, g);
781 oldx = term.c.x, oldy = term.c.y;
782 }
783}
784
785
786void
787draw(int redraw_all) {
788 int x, y;
789 int changed, set;
790
791 if(redraw_all)
792 XClearWindow(xw.dis, xw.win);
793 /* XXX: drawing could be optimised */
794 for(y = 0; y < term.row; y++) {
795 for(x = 0; x < term.col; x++) {
796 changed = term.line[y][x].state & CRupdate;
797 set = term.line[y][x].state & CRset;
798 if((changed && set) || (redraw_all && set)) {
799 term.line[y][x].state &= ~CRupdate;
800 xdrawc(x, y, term.line[y][x]);
801 }
802 }
803 }
804 xcursor(CSdraw);
805}
806
807void
808kpress(XKeyEvent *e) {
809 KeySym ksym;
810 char buf[32];
811 int len;
812 int meta;
813 int shift;
814
815 meta = e->state & Mod4Mask;
816 shift = e->state & ShiftMask;
817 len = XLookupString(e, buf, sizeof(buf), &ksym, NULL);
818 if(len > 0) {
819 buf[sizeof(buf)-1] = '\0';
820 if(meta && len == 1)
821 ttywrite("\033", 1);
822 ttywrite(buf, len);
823 return;
824 }
825 switch(ksym) {
826#ifdef DEBUG1
827 default:
828 printf("errkey: %d\n", (int)ksym);
829 break;
830#endif
831 case XK_Up:
832 case XK_Down:
833 case XK_Left:
834 case XK_Right:
835 sprintf(buf, "\033[%c", "DACB"[ksym - XK_Left]);
836 ttywrite(buf, 3);
837 break;
838 case XK_Delete: ttywrite(KEYDELETE, sizeof(KEYDELETE)-1); break;
839 case XK_Home: ttywrite( KEYHOME, sizeof( KEYHOME)-1); break;
840 case XK_End: ttywrite( KEYEND, sizeof( KEYEND)-1); break;
841 case XK_Prior: ttywrite( KEYPREV, sizeof( KEYPREV)-1); break;
842 case XK_Next: ttywrite( KEYNEXT, sizeof( KEYNEXT)-1); break;
843 case XK_Insert:
844 /* XXX: paste X clipboard */
845 if(shift);
846 break;
847 }
848}
849
850void
851resize(XEvent *e) {
852 int col, row;
853 col = e->xconfigure.width / xw.cw;
854 row = e->xconfigure.height / xw.ch;
855
856 if(term.col != col && term.row != row) {
857 tresize(col, row);
858 ttyresize(col, row);
859 xw.w = e->xconfigure.width;
860 xw.h = e->xconfigure.height;
861 draw(SCredraw);
862 }
863}
864
865
866void
867run(void) {
868 int ret;
869 XEvent ev;
870 fd_set rfd;
871 struct timeval tv = {0, 10000};
872
873 running = 1;
874 XSelectInput(xw.dis, xw.win, ExposureMask | KeyPressMask | StructureNotifyMask);
875 XResizeWindow(xw.dis, xw.win, xw.w , xw.h); /* seems to fix the resize bug in wmii */
876 while(running) {
877 while(XPending(xw.dis)) {
878 XNextEvent(xw.dis, &ev);
879 switch (ev.type) {
880 default:
881 break;
882 case KeyPress:
883 kpress(&ev.xkey);
884 break;
885 case Expose:
886 draw(SCredraw);
887 break;
888 case ConfigureNotify:
889 resize(&ev);
890 break;
891 }
892 }
893 FD_ZERO(&rfd);
894 FD_SET(cmdfd, &rfd);
895 ret = select(cmdfd+1, &rfd, NULL, NULL, &tv);
896 if(ret < 0) {
897 fprintf(stderr, "select: %m\n");
898 running = 0;
899 }
900 if(!ret)
901 continue;
902 if(FD_ISSET(cmdfd, &rfd)) {
903 ttyread();
904 draw(SCupdate);
905 }
906 }
907}
908
909int
910main(int argc, char *argv[]) {
911 if(argc == 2 && !strncmp("-v", argv[1], 3))
912 die("st-"VERSION", © 2009 st engineers\n");
913 else if(argc != 1)
914 die("usage: st [-v]\n");
915 setlocale(LC_CTYPE, "");
916 tnew(80, 24);
917 ttynew();
918 xinit();
919 run();
16 return 0; 920 return 0;
17} 921}
diff --git a/st.h b/st.h
new file mode 100644
index 0000000..be39ef4
--- /dev/null
+++ b/st.h
@@ -0,0 +1,181 @@
1/* See LICENSE for licence details. */
2
3#define _XOPEN_SOURCE
4#include <ctype.h>
5#include <fcntl.h>
6#include <locale.h>
7#include <stdarg.h>
8#include <stdio.h>
9#include <stdlib.h>
10#include <string.h>
11#include <unistd.h>
12#include <sys/types.h>
13#include <sys/stat.h>
14#include <sys/select.h>
15#include <sys/ioctl.h>
16#include <X11/Xlib.h>
17#include <X11/keysym.h>
18#include <X11/Xutil.h>
19
20/* special keys */
21#define KEYDELETE "\033[3~"
22#define KEYHOME "\033[1~"
23#define KEYEND "\033[4~"
24#define KEYPREV "\033[5~"
25#define KEYNEXT "\033[6~"
26
27#define TNAME "st"
28#define SHELL "/bin/bash"
29#define TAB 8
30
31#define FONT "-*-terminus-medium-r-normal-*-14-*-*-*-*-*-*-*"
32#define BORDER 3
33#define LINESPACE 1 /* additional pixel between each line */
34
35/* Default colors */
36#define DefaultFG 7
37#define DefaultBG 0
38#define DefaultCS 1
39#define BellCol DefaultFG /* visual bell color */
40
41static char* colorname[] = {
42 "black",
43 "red",
44 "green",
45 "yellow",
46 "blue",
47 "magenta",
48 "cyan",
49 "white",
50};
51
52
53/* Arbitrary sizes */
54#define ESCSIZ 256
55#define ESCARG 16
56
57#define MIN(a, b) ((a) < (b) ? (a) : (b))
58#define MAX(a, b) ((a) < (b) ? (b) : (a))
59#define LEN(a) (sizeof(a) / sizeof(a[0]))
60#define DEFAULT(a, b) (a) = (a) ? (a) : (b)
61#define BETWEEN(x, a, b) ((a) <= (x) && (x) <= (b))
62#define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x)
63
64
65enum { ATnone=0 , ATreverse=1 , ATunderline=2, ATbold=4 }; /* Attribute */
66enum { CSup, CSdown, CSright, CSleft, CShide, CSdraw, CSwrap, CSsave, CSload }; /* Cursor */
67enum { CRset=1 , CRupdate=2 }; /* Character state */
68enum { TMwrap=1 , TMinsert=2 }; /* Terminal mode */
69enum { SCupdate, SCredraw }; /* screen draw mode */
70
71#ifdef TRUECOLOR
72#error Truecolor not implemented yet
73typedef int Color;
74#else
75typedef char Color;
76#endif
77
78
79typedef struct {
80 char c; /* character code */
81 char mode; /* attribute flags */
82 Color fg; /* foreground */
83 Color bg; /* background */
84 char state; /* state flag */
85} Glyph;
86
87typedef Glyph* Line;
88
89typedef struct {
90 Glyph attr; /* current char attributes */
91 char hidden;
92 int x;
93 int y;
94} TCursor;
95
96/* Escape sequence structs */
97typedef struct {
98 char buf[ESCSIZ+1]; /* raw string */
99 int len; /* raw string length */
100 /* ESC <pre> [[ [<priv>] <arg> [;]] <mode>] */
101 char pre;
102 char priv;
103 int arg[ESCARG+1];
104 int narg; /* nb of args */
105 char mode;
106} Escseq;
107
108/* Internal representation of the screen */
109typedef struct {
110 int row; /* nb row */
111 int col; /* nb col */
112 Line* line; /* screen */
113 TCursor c; /* cursor */
114 int top; /* top scroll limit */
115 int bot; /* bottom scroll limit */
116 int mode; /* terminal mode */
117} Term;
118
119/* Purely graphic info */
120typedef struct {
121 Display* dis;
122 Window win;
123 int scr;
124 int w; /* window width */
125 int h; /* window height */
126 int ch; /* char height */
127 int cw; /* char width */
128} XWindow;
129
130/* Drawing Context */
131typedef struct {
132 unsigned long col[LEN(colorname)];
133 XFontStruct* font;
134 GC gc;
135} DC;
136
137
138void die(const char *errstr, ...);
139void draw(int);
140void execsh(void);
141void kpress(XKeyEvent *);
142void resize(XEvent *);
143void run(void);
144
145int escaddc(char);
146int escfinal(char);
147void escdump(void);
148void eschandle(void);
149void escparse(void);
150void escreset(void);
151
152void tclearregion(int, int, int, int);
153void tcpos(int);
154void tcursor(int);
155void tdeletechar(int);
156void tdeleteline(int);
157void tdump(void);
158void tinsertblank(int);
159void tinsertblankline(int);
160void tmoveto(int, int);
161void tnew(int, int);
162void tnewline(void);
163void tputc(char);
164void tputs(char*, int);
165void tresize(int, int);
166void tscroll(void);
167void tsetattr(int*, int);
168void tsetchar(char);
169void tsetscroll(int, int);
170
171void ttynew(void);
172void ttyread(void);
173void ttyresize(int, int);
174void ttywrite(char *, size_t);
175
176unsigned long xgetcol(const char *);
177void xclear(int, int, int, int);
178void xcursor(int);
179void xdrawc(int, int, Glyph);
180void xinit(void);
181void xscroll(void);
diff --git a/st.info b/st.info
new file mode 100644
index 0000000..1ded6b5
--- /dev/null
+++ b/st.info
@@ -0,0 +1,54 @@
1# Reconstructed via infocmp from file: /lib/terminfo/p/pcansi
2st| simpleterm,
3 am,
4 ul,
5 mir,
6 msgr,
7 colors#8,
8 cols#80,
9 it#8,
10 lines#24,
11 ncv#3,
12 pairs#64,
13 acsc=*`#aof+g+j+k+l+m+n-o-p-q-r-s+t+u+v+w|x<y>z{{||}}-~,
14 bel=^G,
15 bold=\E[1m,
16 cbt=\E[Z,
17 clear=\E[H\E[2J,
18 cr=^M,
19 cub1=\E[D,
20 cud1=\E[B,
21 cuf1=\E[C,
22 cup=\E[%i%p1%d;%p2%dH,
23 cuu1=\E[A,
24 dch1=\E[P,
25 dl1=\E[M,
26 ed=\E[J,
27 el=\E[K,
28 home=\E[H,
29 ht=^I,
30 hts=\EH,
31 il1=\E[L,
32 ind=^J,
33 invis=\E[8m,
34 kbs=^H,
35 kcub1=\E[D,
36 kcud1=\E[B,
37 kcuf1=\E[C,
38 kcuu1=\E[A,
39 khome=\E[1~,
40 knp=\E[6~,
41 kpp=\E[5~,
42 op=\E[37;40m,
43 rev=\E[7m,
44 rmacs=\E[10m,
45 rmso=\E[m,
46 rmul=\E[m,
47 setab=\E[4%p1%dm,
48 setaf=\E[3%p1%dm,
49# sgr=\E[0;10%?%p1%t;7%;%?%p2%t;4%;%?%p3%t;7%;%?%p4%t;5%;%?%p6%t;1%;%?%p7%t;8%;%?%p9%t;12%;m,
50 sgr0=\E[0m,
51 smacs=\E[12m,
52 smso=\E[7m,
53 smul=\E[4m,
54 tbc=\E[2g,
diff --git a/std.c b/std.c
deleted file mode 100644
index 55da9d6..0000000
--- a/std.c
+++ /dev/null
@@ -1,363 +0,0 @@
1/* See LICENSE file for copyright and license details.
2 *
3 * Simple terminal daemon is a terminal emulator. It can be used in
4 * combination with simple terminal to emulate a mostly VT100-compatible
5 * terminal.
6 *
7 * In this process std works like a filter. It reads data from a
8 * pseudo-terminal and parses the escape sequences and transforms them
9 * into an ed(1)-like language. The resulting data is buffered and
10 * written to stdout.
11 * Parallely it reads data from stdin and parses and executes the
12 * commands. The resulting data is written to the pseudo-terminal.
13 */
14#include <sys/types.h>
15#include <sys/wait.h>
16#include <ctype.h>
17#include <err.h>
18#include <errno.h>
19#include <fcntl.h>
20#if !(_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600)
21#include <pty.h>
22#endif
23#include <signal.h>
24#include <stdarg.h>
25#include <stdio.h>
26#include <stdlib.h>
27#include <string.h>
28#include <unistd.h>
29
30#define LENGTH(x) (sizeof(x) / sizeof((x)[0]))
31#define MAX(a,b) (((a) > (b)) ? (a) : (b))
32#define MIN(a,b) (((a) < (b)) ? (a) : (b))
33
34typedef struct {
35 unsigned char data[BUFSIZ];
36 int s, e;
37 int n;
38} RingBuffer;
39
40static void buffer(char c);
41static void cmd(const char *cmdstr, ...);
42static void getpty(void);
43static void movea(int x, int y);
44static void mover(int x, int y);
45static void parsecmd(void);
46static void parseesc(void);
47static void scroll(int l);
48static void shell(void);
49static void sigchld(int n);
50static char unbuffer(void);
51
52static int cols = 80, lines = 25;
53static int cx = 0, cy = 0;
54static int c;
55static int ptm, pts;
56static _Bool bold, digit, qmark;
57static pid_t pid;
58static RingBuffer buf;
59static FILE *fptm;
60
61void
62buffer(char c) {
63 if(buf.n < LENGTH(buf.data))
64 buf.n++;
65 else
66 buf.s = (buf.s + 1) % LENGTH(buf.data);
67 buf.data[buf.e++] = c;
68 buf.e %= LENGTH(buf.data);
69}
70
71void
72cmd(const char *cmdstr, ...) {
73 va_list ap;
74
75 putchar('\n');
76 putchar(':');
77 va_start(ap, cmdstr);
78 vfprintf(stdout, cmdstr, ap);
79 va_end(ap);
80}
81
82void
83movea(int x, int y) {
84 x = MAX(x, cols);
85 y = MAX(y, lines);
86 cx = x;
87 cy = y;
88 cmd("seek(%d,%d)", x, y);
89}
90
91void
92mover(int x, int y) {
93 movea(cx + x, cy + y);
94}
95
96void
97parsecmd(void) {
98}
99
100void
101parseesc(void) {
102 int i, j;
103 int arg[16];
104
105 memset(arg, 0, LENGTH(arg));
106 c = getc(fptm);
107 switch(c) {
108 case '[':
109 c = getc(fptm);
110 for(j = 0; j < LENGTH(arg);) {
111 if(isdigit(c)) {
112 digit = 1;
113 arg[j] *= 10;
114 arg[j] += c - '0';
115 }
116 else if(c == '?')
117 qmark = 1;
118 else if(c == ';') {
119 if(!digit)
120 errx(EXIT_FAILURE, "syntax error");
121 digit = 0;
122 j++;
123 }
124 else {
125 if(digit) {
126 digit = 0;
127 j++;
128 }
129 break;
130 }
131 c = getc(fptm);
132 }
133 switch(c) {
134 case '@':
135 break;
136 case 'A':
137 mover(0, j ? arg[0] : 1);
138 break;
139 case 'B':
140 mover(0, j ? -arg[0] : -1);
141 break;
142 case 'C':
143 mover(j ? arg[0] : 1, 0);
144 break;
145 case 'D':
146 mover(j ? -arg[0] : -1, 0);
147 break;
148 case 'E':
149 /* movel(j ? arg[0] : 1); */
150 break;
151 case 'F':
152 /* movel(j ? -arg[0] : -1); */
153 break;
154 case '`':
155 case 'G':
156 movea(j ? arg[0] : 1, cy);
157 break;
158 case 'f':
159 case 'H':
160 movea(arg[1] ? arg[1] : 1, arg[0] ? arg[0] : 1);
161 case 'L':
162 /* insline(j ? arg[0] : 1); */
163 break;
164 case 'M':
165 /* delline(j ? arg[0] : 1); */
166 break;
167 case 'P':
168 break;
169 case 'S':
170 scroll(j ? arg[0] : 1);
171 break;
172 case 'T':
173 scroll(j ? -arg[0] : -1);
174 break;
175 case 'd':
176 movea(cx, j ? arg[0] : 1);
177 break;
178 case 'm':
179 for(i = 0; i < j; i++) {
180 if(arg[i] >= 30 && arg[i] <= 37)
181 cmd("#%d", arg[i] - 30);
182 if(arg[i] >= 40 && arg[i] <= 47)
183 cmd("|%d", arg[i] - 40);
184 /* xterm bright colors */
185 if(arg[i] >= 90 && arg[i] <= 97)
186 cmd("#%d", arg[i] - 90);
187 if(arg[i] >= 100 && arg[i] <= 107)
188 cmd("|%d", arg[i] - 100);
189 switch(arg[i]) {
190 case 0:
191 case 22:
192 if(bold)
193 cmd("bold");
194 case 1:
195 if(!bold)
196 cmd("bold");
197 break;
198 }
199 }
200 break;
201 }
202 break;
203 default:
204 putchar('\033');
205 ungetc(c, fptm);
206 }
207}
208
209void
210scroll(int l) {
211 cmd("seek(%d,%d)", cx, cy + l);
212}
213
214void
215getpty(void) {
216 char *ptsdev;
217
218#if defined(_GNU_SOURCE)
219 ptm = getpt();
220#elif _POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600
221 ptm = posix_openpt(O_RDWR);
222#else
223 ptm = open("/dev/ptmx", O_RDWR);
224 if(ptm == -1)
225 if(openpty(&ptm, &pts, NULL, NULL, NULL) == -1)
226 err(EXIT_FAILURE, "cannot open pty");
227#endif
228#if defined(_XOPEN_SOURCE)
229 if(ptm != -1) {
230 if(grantpt(ptm) == -1)
231 err(EXIT_FAILURE, "cannot grant access to pty");
232 if(unlockpt(ptm) == -1)
233 err(EXIT_FAILURE, "cannot unlock pty");
234 ptsdev = ptsname(ptm);
235 if(!ptsdev)
236 err(EXIT_FAILURE, "slave pty name undefined");
237 pts = open(ptsdev, O_RDWR);
238 if(pts == -1)
239 err(EXIT_FAILURE, "cannot open slave pty");
240 }
241 else
242 err(EXIT_FAILURE, "cannot open pty");
243#endif
244}
245
246void
247shell(void) {
248 static char *shell = NULL;
249
250 if(!shell && !(shell = getenv("SHELL")))
251 shell = "/bin/sh";
252 pid = fork();
253 switch(pid) {
254 case -1:
255 err(EXIT_FAILURE, "cannot fork");
256 case 0:
257 setsid();
258 dup2(pts, STDIN_FILENO);
259 dup2(pts, STDOUT_FILENO);
260 dup2(pts, STDERR_FILENO);
261 close(ptm);
262 putenv("TERM=vt102");
263 execvp(shell, NULL);
264 break;
265 default:
266 close(pts);
267 signal(SIGCHLD, sigchld);
268 }
269}
270
271void
272sigchld(int n) {
273 int ret;
274
275 if(waitpid(pid, &ret, 0) == -1)
276 err(EXIT_FAILURE, "waiting for child failed");
277 if(WIFEXITED(ret))
278 exit(WEXITSTATUS(ret));
279 else
280 exit(EXIT_SUCCESS);
281}
282
283char
284unbuffer(void) {
285 char c;
286
287 c = buf.data[buf.s++];
288 buf.s %= LENGTH(buf.data);
289 buf.n--;
290 return c;
291}
292
293int
294main(int argc, char *argv[]) {
295 fd_set rfds;
296
297 if(argc == 2 && !strcmp("-v", argv[1]))
298 errx(EXIT_SUCCESS, "std-"VERSION", © 2008 Matthias-Christian Ott");
299 else if(argc == 1)
300 errx(EXIT_FAILURE, "usage: std [-v]");
301 getpty();
302 shell();
303 FD_ZERO(&rfds);
304 FD_SET(STDIN_FILENO, &rfds);
305 FD_SET(ptm, &rfds);
306 if(!(fptm = fdopen(ptm, "r+")))
307 err(EXIT_FAILURE, "cannot open pty");
308 if(fcntl(ptm, F_SETFL, O_NONBLOCK) == -1)
309 err(EXIT_FAILURE, "cannot set pty to non-blocking mode");
310 if(fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK) == -1)
311 err(EXIT_FAILURE, "cannot set stdin to non-blocking mode");
312 for(;;) {
313 if(select(MAX(ptm, STDIN_FILENO) + 1, &rfds, NULL, NULL, NULL) == -1)
314 err(EXIT_FAILURE, "cannot select");
315 if(FD_ISSET(ptm, &rfds)) {
316 for(;;) {
317 if((c = getc(fptm)) == EOF) {
318 if(feof(fptm)) {
319 FD_CLR(ptm, &rfds);
320 fflush(fptm);
321 break;
322 }
323 if(errno != EAGAIN)
324 err(EXIT_FAILURE, "cannot read from pty");
325 fflush(stdout);
326 break;
327 }
328 switch(c) {
329 case '\033':
330 parseesc();
331 break;
332 default:
333 putchar(c);
334 }
335 }
336 fflush(stdout);
337 }
338 if(FD_ISSET(STDIN_FILENO, &rfds)) {
339 for(;;) {
340 if((c = getchar()) == EOF) {
341 if(feof(stdin)) {
342 FD_CLR(STDIN_FILENO, &rfds);
343 fflush(fptm);
344 break;
345 }
346 if(errno != EAGAIN)
347 err(EXIT_FAILURE, "cannot read from stdin");
348 fflush(fptm);
349 break;
350 }
351 switch(c) {
352 case ':':
353 parsecmd();
354 break;
355 default:
356 putc(c, fptm);
357 }
358 }
359 fflush(fptm);
360 }
361 }
362 return 0;
363}