b0142fe34b5a397719f714b765cda0472f982ac1
[st.git] / std.c
1 /* See LICENSE file for copyright and license details. */
2 #include "util.h"
3 #include <sys/types.h>
4 #include <sys/wait.h>
5 #include <ctype.h>
6 #include <signal.h>
7 #include <stdarg.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <unistd.h>
12
13 #define LENGTH(x)       (sizeof(x) / sizeof((x)[0]))
14 #define MAX(a,b)        (((a) > (b)) ? (a) : (b))
15 #define MIN(a,b)        (((a) < (b)) ? (a) : (b))
16
17 static void buffer(char c);
18 static void cmd(const char *cmdstr, ...);
19 static int getch();
20 void getpty(void);
21 static void movea(int x, int y);
22 static void mover(int x, int y);
23 static void parseesc(void);
24 static void scroll(int l);
25 static void shell(void);
26 static void sigchld(int n);
27 static char unbuffer(void);
28 static void ungetch(int c);
29
30 typedef struct {
31         unsigned char data[BUFSIZ];
32         int s, e;
33         int n;
34 } RingBuffer;
35
36 typedef struct {
37         unsigned char data[BUFSIZ];
38         int i, n;
39 } ReadBuffer;
40
41 static int cols = 80, lines = 25;
42 static int cx = 0, cy = 0;
43 static int c;
44 int ptm, pts;
45 static _Bool bold, digit, qmark;
46 static pid_t pid;
47 static RingBuffer buf;
48 static ReadBuffer rbuf;
49
50 void
51 buffer(char c) {
52         if(buf.n < LENGTH(buf.data))
53                 buf.n++;
54         else
55                 buf.s = (buf.s + 1) % LENGTH(buf.data);
56         buf.data[buf.e++] = c;
57         buf.e %= LENGTH(buf.data);
58 }
59
60 void
61 cmd(const char *cmdstr, ...) {
62         va_list ap;
63
64         putchar('\n');
65         putchar(':');
66         va_start(ap, cmdstr);
67         vfprintf(stdout, cmdstr, ap);
68         va_end(ap);
69 }
70
71 int
72 getch() {
73         if(rbuf.i++ >= rbuf.n) {
74                 rbuf.n = read(ptm, rbuf.data, LENGTH(rbuf.data));
75                 if(rbuf.n == -1)
76                         eprintn("error, cannot read from slave pty");
77                 rbuf.i = 0;
78         }
79         return rbuf.data[rbuf.i];
80 }
81
82 void
83 movea(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
91 void
92 mover(int x, int y) {
93         movea(cx + x, cy + y);
94 }
95
96 void
97 parseesc(void) {
98         int i, j;
99         int arg[16];
100
101         memset(arg, 0, LENGTH(arg));
102         c = getch();
103         switch(c) {
104         case '[':
105                 c = getch();
106                 for(j = 0; j < LENGTH(arg);) {
107                         if(isdigit(c)) {
108                                 digit = 1;
109                                 arg[j] *= 10;
110                                 arg[j] += c - '0';
111                         }
112                         else if(c == '?')
113                                 qmark = 1;
114                         else if(c == ';') {
115                                 if(!digit)
116                                         eprint("syntax error\n");
117                                 digit = 0;
118                                 j++;
119                         }
120                         else {
121                                 if(digit) {
122                                         digit = 0;
123                                         j++;
124                                 }
125                                 break;
126                         }
127                         c = getch();
128                 }
129                 switch(c) {
130                 case '@':
131                         break;
132                 case 'A':
133                         mover(0, j ? arg[0] : 1);
134                         break;
135                 case 'B':
136                         mover(0, j ? -arg[0] : -1);
137                         break;
138                 case 'C':
139                         mover(j ? arg[0] : 1, 0);
140                         break;
141                 case 'D':
142                         mover(j ? -arg[0] : -1, 0);
143                         break;
144                 case 'E':
145                         /* movel(j ? arg[0] : 1); */
146                         break;
147                 case 'F':
148                         /* movel(j ? -arg[0] : -1); */
149                         break;
150                 case '`':
151                 case 'G':
152                         movea(j ? arg[0] : 1, cy);
153                         break;
154                 case 'f':
155                 case 'H':
156                         movea(arg[1] ? arg[1] : 1, arg[0] ? arg[0] : 1);
157                 case 'L':
158                         /* insline(j ? arg[0] : 1); */
159                         break;
160                 case 'M':
161                         /* delline(j ? arg[0] : 1); */
162                         break;
163                 case 'P':
164                         break;
165                 case 'S':
166                         scroll(j ? arg[0] : 1);
167                         break;
168                 case 'T':
169                         scroll(j ? -arg[0] : -1);
170                         break;
171                 case 'd':
172                         movea(cx, j ? arg[0] : 1);
173                         break;
174                 case 'm':
175                         for(i = 0; i < j; i++) {
176                                 if(arg[i] >= 30 && arg[i] <= 37)
177                                         cmd("#%d", arg[i] - 30);
178                                 if(arg[i] >= 40 && arg[i] <= 47)
179                                         cmd("|%d", arg[i] - 40);
180                                 /* xterm bright colors */
181                                 if(arg[i] >= 90 && arg[i] <= 97)
182                                         cmd("#%d", arg[i] - 90);
183                                 if(arg[i] >= 100 && arg[i] <= 107)
184                                         cmd("|%d", arg[i] - 100);
185                                 switch(arg[i]) {
186                                 case 0:
187                                 case 22:
188                                         if(bold)
189                                                 cmd("bold");
190                                 case 1:
191                                         if(!bold)
192                                                 cmd("bold");
193                                         break;
194                                 }
195                         }
196                         break;
197                 }
198                 break;
199         default:
200                 putchar('\033');
201                 ungetch(c);
202         }
203 }
204
205 void
206 scroll(int l) {
207         cmd("seek(%d,%d)", cx, cy + l);
208 }
209
210 void
211 shell(void) {
212         static char *shell = NULL;
213
214         if(!shell && !(shell = getenv("SHELL")))
215                 shell = "/bin/sh";
216         pid = fork();
217         switch(pid) {
218         case -1:
219                 eprint("error, cannot fork\n");
220         case 0:
221                 setsid();
222                 dup2(pts, STDIN_FILENO);
223                 dup2(pts, STDOUT_FILENO);
224                 dup2(pts, STDERR_FILENO);
225                 close(ptm);
226                 putenv("TERM=vt102");
227                 execvp(shell, NULL);
228                 break;
229         default:
230                 close(pts);
231                 signal(SIGCHLD, sigchld);
232         }
233 }
234
235 void
236 sigchld(int n) {
237         int ret;
238
239         if(waitpid(pid, &ret, 0) == -1)
240                 eprintn("error, waiting for child failed");
241         if(WIFEXITED(ret))
242                 exit(WEXITSTATUS(ret));
243         else
244                 exit(EXIT_SUCCESS);
245 }
246
247 char
248 unbuffer(void) {
249         char c;
250
251         c = buf.data[buf.s++];
252         buf.s %= LENGTH(buf.data);
253         buf.n--;
254         return c;
255 }
256
257 void
258 ungetch(int c) {
259         if(rbuf.i + 1 >= rbuf.n)
260                 eprint("error, read buffer full\n");
261         rbuf.data[rbuf.i++] = c;
262 }
263
264 int
265 main(int argc, char *argv[]) {
266         fd_set rfds;
267         int r;
268
269         if(argc == 2 && !strcmp("-v", argv[1]))
270                 eprint("std-"VERSION", © 2008 Matthias-Christian Ott\n");
271         else if(argc == 1)
272                 eprint("usage: st [-v]\n");
273         getpty();
274         shell();
275         FD_ZERO(&rfds);
276         FD_SET(STDIN_FILENO, &rfds);
277         FD_SET(ptm, &rfds);
278         for(;;) {
279                 r = select(ptm + 1, &rfds, NULL, NULL, NULL);
280                 if(r == -1)
281                         eprintn("error, cannot select");
282                 if(FD_ISSET(ptm, &rfds))
283                         do {
284                                 c = getch();
285                                 switch(c) {
286                                 case '\033':
287                                         parseesc();
288                                         break;
289                                 default:
290                                         putchar(c);
291                                 }
292                                 fflush(stdout);
293                         } while(rbuf.i < rbuf.n);
294         }
295         return 0;
296 }