JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
daf759e5d4f444999e7c5f522559c7759ba062f0
[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 }