Xinqi Bao's Git

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