Xinqi Bao's Git

make ptm and pts static
[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 <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 static 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 err(EXIT_FAILURE, "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 errx(EXIT_FAILURE, "syntax error");
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 err(EXIT_FAILURE, "cannot fork");
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 err(EXIT_FAILURE, "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 errx(EXIT_FAILURE, "read buffer full");
261 rbuf.data[rbuf.i++] = c;
262 }
263
264 int
265 main(int argc, char *argv[]) {
266 fd_set rfds;
267
268 if(argc == 2 && !strcmp("-v", argv[1])) {
269 fprintf(stderr, "std-"VERSION", © 2008 Matthias-Christian Ott\n");
270 exit(EXIT_SUCCESS);
271 }
272 else if(argc == 1) {
273 fprintf(stderr, "usage: st [-v]\n");
274 exit(EXIT_FAILURE);
275 }
276 getpty();
277 shell();
278 FD_ZERO(&rfds);
279 FD_SET(STDIN_FILENO, &rfds);
280 FD_SET(ptm, &rfds);
281 for(;;) {
282 if(select(ptm + 1, &rfds, NULL, NULL, NULL) == -1)
283 err(EXIT_FAILURE, "cannot select");
284 if(FD_ISSET(ptm, &rfds)) {
285 do {
286 c = getch();
287 switch(c) {
288 case '\033':
289 parseesc();
290 break;
291 default:
292 putchar(c);
293 }
294 } while(rbuf.i < rbuf.n);
295 fflush(stdout);
296 }
297 }
298 return 0;
299 }