Xinqi Bao's Git

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