Xinqi Bao's Git

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