Xinqi Bao's Git

add select(2)-based i/o multiplexing
[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 void buffer(char c);
18 void cmd(const char *cmdstr, ...);
19 int getch();
20 void getpty(void);
21 void movea(int x, int y);
22 void mover(int x, int y);
23 void parseesc(void);
24 void scroll(int l);
25 void shell(void);
26 void sigchld(int n);
27 char unbuffer(void);
28 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 int cols = 80, lines = 25;
42 int cx = 0, cy = 0;
43 int c;
44 int ptm, pts;
45 _Bool bold, digit, qmark;
46 pid_t pid;
47 RingBuffer buf;
48 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("s %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("b");
190 case 1:
191 if(!bold)
192 cmd("b");
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("s %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 c = getch();
284 switch(c) {
285 case '\033':
286 parseesc();
287 break;
288 default:
289 putchar(c);
290 }
291 fflush(stdout);
292 }
293 }
294 return 0;
295 }