diff options
| author | Anselm R Garbe <garbeam@gmail.com> | 2009-05-10 13:17:09 +0100 |
|---|---|---|
| committer | Anselm R Garbe <garbeam@gmail.com> | 2009-05-10 13:17:09 +0100 |
| commit | d58dd3b8bc42ed31232e4145696d7dacb117a31c (patch) | |
| tree | dfe3362a600e9491a687cd765158b70d3328333d /std.c | |
| parent | add accidentally omitted word (diff) | |
| download | st-d58dd3b8bc42ed31232e4145696d7dacb117a31c.tar.gz st-d58dd3b8bc42ed31232e4145696d7dacb117a31c.zip | |
backport of local changes
Diffstat (limited to 'std.c')
| -rw-r--r-- | std.c | 363 |
1 files changed, 0 insertions, 363 deletions
| @@ -1,363 +0,0 @@ | |||
| 1 | /* See LICENSE file for copyright and license details. | ||
| 2 | * | ||
| 3 | * Simple terminal daemon is a terminal emulator. It can be used in | ||
| 4 | * combination with simple terminal to emulate a mostly VT100-compatible | ||
| 5 | * terminal. | ||
| 6 | * | ||
| 7 | * In this process std works like a filter. It reads data from a | ||
| 8 | * pseudo-terminal and parses the escape sequences and transforms them | ||
| 9 | * into an ed(1)-like language. The resulting data is buffered and | ||
| 10 | * written to stdout. | ||
| 11 | * Parallely it reads data from stdin and parses and executes the | ||
| 12 | * commands. The resulting data is written to the pseudo-terminal. | ||
| 13 | */ | ||
| 14 | #include <sys/types.h> | ||
| 15 | #include <sys/wait.h> | ||
| 16 | #include <ctype.h> | ||
| 17 | #include <err.h> | ||
| 18 | #include <errno.h> | ||
| 19 | #include <fcntl.h> | ||
| 20 | #if !(_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600) | ||
| 21 | #include <pty.h> | ||
| 22 | #endif | ||
| 23 | #include <signal.h> | ||
| 24 | #include <stdarg.h> | ||
| 25 | #include <stdio.h> | ||
| 26 | #include <stdlib.h> | ||
| 27 | #include <string.h> | ||
| 28 | #include <unistd.h> | ||
| 29 | |||
| 30 | #define LENGTH(x) (sizeof(x) / sizeof((x)[0])) | ||
| 31 | #define MAX(a,b) (((a) > (b)) ? (a) : (b)) | ||
| 32 | #define MIN(a,b) (((a) < (b)) ? (a) : (b)) | ||
| 33 | |||
| 34 | typedef struct { | ||
| 35 | unsigned char data[BUFSIZ]; | ||
| 36 | int s, e; | ||
| 37 | int n; | ||
| 38 | } RingBuffer; | ||
| 39 | |||
| 40 | static void buffer(char c); | ||
| 41 | static void cmd(const char *cmdstr, ...); | ||
| 42 | static void getpty(void); | ||
| 43 | static void movea(int x, int y); | ||
| 44 | static void mover(int x, int y); | ||
| 45 | static void parsecmd(void); | ||
| 46 | static void parseesc(void); | ||
| 47 | static void scroll(int l); | ||
| 48 | static void shell(void); | ||
| 49 | static void sigchld(int n); | ||
| 50 | static char unbuffer(void); | ||
| 51 | |||
| 52 | static int cols = 80, lines = 25; | ||
| 53 | static int cx = 0, cy = 0; | ||
| 54 | static int c; | ||
| 55 | static int ptm, pts; | ||
| 56 | static _Bool bold, digit, qmark; | ||
| 57 | static pid_t pid; | ||
| 58 | static RingBuffer buf; | ||
| 59 | static FILE *fptm; | ||
| 60 | |||
| 61 | void | ||
| 62 | buffer(char c) { | ||
| 63 | if(buf.n < LENGTH(buf.data)) | ||
| 64 | buf.n++; | ||
| 65 | else | ||
| 66 | buf.s = (buf.s + 1) % LENGTH(buf.data); | ||
| 67 | buf.data[buf.e++] = c; | ||
| 68 | buf.e %= LENGTH(buf.data); | ||
| 69 | } | ||
| 70 | |||
| 71 | void | ||
| 72 | cmd(const char *cmdstr, ...) { | ||
| 73 | va_list ap; | ||
| 74 | |||
| 75 | putchar('\n'); | ||
| 76 | putchar(':'); | ||
| 77 | va_start(ap, cmdstr); | ||
| 78 | vfprintf(stdout, cmdstr, ap); | ||
| 79 | va_end(ap); | ||
| 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 | parsecmd(void) { | ||
| 98 | } | ||
| 99 | |||
| 100 | void | ||
| 101 | parseesc(void) { | ||
| 102 | int i, j; | ||
| 103 | int arg[16]; | ||
| 104 | |||
| 105 | memset(arg, 0, LENGTH(arg)); | ||
| 106 | c = getc(fptm); | ||
| 107 | switch(c) { | ||
| 108 | case '[': | ||
| 109 | c = getc(fptm); | ||
| 110 | for(j = 0; j < LENGTH(arg);) { | ||
| 111 | if(isdigit(c)) { | ||
| 112 | digit = 1; | ||
| 113 | arg[j] *= 10; | ||
| 114 | arg[j] += c - '0'; | ||
| 115 | } | ||
| 116 | else if(c == '?') | ||
| 117 | qmark = 1; | ||
| 118 | else if(c == ';') { | ||
| 119 | if(!digit) | ||
| 120 | errx(EXIT_FAILURE, "syntax error"); | ||
| 121 | digit = 0; | ||
| 122 | j++; | ||
| 123 | } | ||
| 124 | else { | ||
| 125 | if(digit) { | ||
| 126 | digit = 0; | ||
| 127 | j++; | ||
| 128 | } | ||
| 129 | break; | ||
| 130 | } | ||
| 131 | c = getc(fptm); | ||
| 132 | } | ||
| 133 | switch(c) { | ||
| 134 | case '@': | ||
| 135 | break; | ||
| 136 | case 'A': | ||
| 137 | mover(0, j ? arg[0] : 1); | ||
| 138 | break; | ||
| 139 | case 'B': | ||
| 140 | mover(0, j ? -arg[0] : -1); | ||
| 141 | break; | ||
| 142 | case 'C': | ||
| 143 | mover(j ? arg[0] : 1, 0); | ||
| 144 | break; | ||
| 145 | case 'D': | ||
| 146 | mover(j ? -arg[0] : -1, 0); | ||
| 147 | break; | ||
| 148 | case 'E': | ||
| 149 | /* movel(j ? arg[0] : 1); */ | ||
| 150 | break; | ||
| 151 | case 'F': | ||
| 152 | /* movel(j ? -arg[0] : -1); */ | ||
| 153 | break; | ||
| 154 | case '`': | ||
| 155 | case 'G': | ||
| 156 | movea(j ? arg[0] : 1, cy); | ||
| 157 | break; | ||
| 158 | case 'f': | ||
| 159 | case 'H': | ||
| 160 | movea(arg[1] ? arg[1] : 1, arg[0] ? arg[0] : 1); | ||
| 161 | case 'L': | ||
| 162 | /* insline(j ? arg[0] : 1); */ | ||
| 163 | break; | ||
| 164 | case 'M': | ||
| 165 | /* delline(j ? arg[0] : 1); */ | ||
| 166 | break; | ||
| 167 | case 'P': | ||
| 168 | break; | ||
| 169 | case 'S': | ||
| 170 | scroll(j ? arg[0] : 1); | ||
| 171 | break; | ||
| 172 | case 'T': | ||
| 173 | scroll(j ? -arg[0] : -1); | ||
| 174 | break; | ||
| 175 | case 'd': | ||
| 176 | movea(cx, j ? arg[0] : 1); | ||
| 177 | break; | ||
| 178 | case 'm': | ||
| 179 | for(i = 0; i < j; i++) { | ||
| 180 | if(arg[i] >= 30 && arg[i] <= 37) | ||
| 181 | cmd("#%d", arg[i] - 30); | ||
| 182 | if(arg[i] >= 40 && arg[i] <= 47) | ||
| 183 | cmd("|%d", arg[i] - 40); | ||
| 184 | /* xterm bright colors */ | ||
| 185 | if(arg[i] >= 90 && arg[i] <= 97) | ||
| 186 | cmd("#%d", arg[i] - 90); | ||
| 187 | if(arg[i] >= 100 && arg[i] <= 107) | ||
| 188 | cmd("|%d", arg[i] - 100); | ||
| 189 | switch(arg[i]) { | ||
| 190 | case 0: | ||
| 191 | case 22: | ||
| 192 | if(bold) | ||
| 193 | cmd("bold"); | ||
| 194 | case 1: | ||
| 195 | if(!bold) | ||
| 196 | cmd("bold"); | ||
| 197 | break; | ||
| 198 | } | ||
| 199 | } | ||
| 200 | break; | ||
| 201 | } | ||
| 202 | break; | ||
| 203 | default: | ||
| 204 | putchar('\033'); | ||
| 205 | ungetc(c, fptm); | ||
| 206 | } | ||
| 207 | } | ||
| 208 | |||
| 209 | void | ||
| 210 | scroll(int l) { | ||
| 211 | cmd("seek(%d,%d)", cx, cy + l); | ||
| 212 | } | ||
| 213 | |||
| 214 | void | ||
| 215 | getpty(void) { | ||
| 216 | char *ptsdev; | ||
| 217 | |||
| 218 | #if defined(_GNU_SOURCE) | ||
| 219 | ptm = getpt(); | ||
| 220 | #elif _POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600 | ||
| 221 | ptm = posix_openpt(O_RDWR); | ||
| 222 | #else | ||
| 223 | ptm = open("/dev/ptmx", O_RDWR); | ||
| 224 | if(ptm == -1) | ||
| 225 | if(openpty(&ptm, &pts, NULL, NULL, NULL) == -1) | ||
| 226 | err(EXIT_FAILURE, "cannot open pty"); | ||
| 227 | #endif | ||
| 228 | #if defined(_XOPEN_SOURCE) | ||
| 229 | if(ptm != -1) { | ||
| 230 | if(grantpt(ptm) == -1) | ||
| 231 | err(EXIT_FAILURE, "cannot grant access to pty"); | ||
| 232 | if(unlockpt(ptm) == -1) | ||
| 233 | err(EXIT_FAILURE, "cannot unlock pty"); | ||
| 234 | ptsdev = ptsname(ptm); | ||
| 235 | if(!ptsdev) | ||
| 236 | err(EXIT_FAILURE, "slave pty name undefined"); | ||
| 237 | pts = open(ptsdev, O_RDWR); | ||
| 238 | if(pts == -1) | ||
| 239 | err(EXIT_FAILURE, "cannot open slave pty"); | ||
| 240 | } | ||
| 241 | else | ||
| 242 | err(EXIT_FAILURE, "cannot open pty"); | ||
| 243 | #endif | ||
| 244 | } | ||
| 245 | |||
| 246 | void | ||
| 247 | shell(void) { | ||
| 248 | static char *shell = NULL; | ||
| 249 | |||
| 250 | if(!shell && !(shell = getenv("SHELL"))) | ||
| 251 | shell = "/bin/sh"; | ||
| 252 | pid = fork(); | ||
| 253 | switch(pid) { | ||
| 254 | case -1: | ||
| 255 | err(EXIT_FAILURE, "cannot fork"); | ||
| 256 | case 0: | ||
| 257 | setsid(); | ||
| 258 | dup2(pts, STDIN_FILENO); | ||
| 259 | dup2(pts, STDOUT_FILENO); | ||
| 260 | dup2(pts, STDERR_FILENO); | ||
| 261 | close(ptm); | ||
| 262 | putenv("TERM=vt102"); | ||
| 263 | execvp(shell, NULL); | ||
| 264 | break; | ||
| 265 | default: | ||
| 266 | close(pts); | ||
| 267 | signal(SIGCHLD, sigchld); | ||
| 268 | } | ||
| 269 | } | ||
| 270 | |||
| 271 | void | ||
| 272 | sigchld(int n) { | ||
| 273 | int ret; | ||
| 274 | |||
| 275 | if(waitpid(pid, &ret, 0) == -1) | ||
| 276 | err(EXIT_FAILURE, "waiting for child failed"); | ||
| 277 | if(WIFEXITED(ret)) | ||
| 278 | exit(WEXITSTATUS(ret)); | ||
| 279 | else | ||
| 280 | exit(EXIT_SUCCESS); | ||
| 281 | } | ||
| 282 | |||
| 283 | char | ||
| 284 | unbuffer(void) { | ||
| 285 | char c; | ||
| 286 | |||
| 287 | c = buf.data[buf.s++]; | ||
| 288 | buf.s %= LENGTH(buf.data); | ||
| 289 | buf.n--; | ||
| 290 | return c; | ||
| 291 | } | ||
| 292 | |||
| 293 | int | ||
| 294 | main(int argc, char *argv[]) { | ||
| 295 | fd_set rfds; | ||
| 296 | |||
| 297 | if(argc == 2 && !strcmp("-v", argv[1])) | ||
| 298 | errx(EXIT_SUCCESS, "std-"VERSION", © 2008 Matthias-Christian Ott"); | ||
| 299 | else if(argc == 1) | ||
| 300 | errx(EXIT_FAILURE, "usage: std [-v]"); | ||
| 301 | getpty(); | ||
| 302 | shell(); | ||
| 303 | FD_ZERO(&rfds); | ||
| 304 | FD_SET(STDIN_FILENO, &rfds); | ||
| 305 | FD_SET(ptm, &rfds); | ||
| 306 | if(!(fptm = fdopen(ptm, "r+"))) | ||
| 307 | err(EXIT_FAILURE, "cannot open pty"); | ||
| 308 | if(fcntl(ptm, F_SETFL, O_NONBLOCK) == -1) | ||
| 309 | err(EXIT_FAILURE, "cannot set pty to non-blocking mode"); | ||
| 310 | if(fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK) == -1) | ||
| 311 | err(EXIT_FAILURE, "cannot set stdin to non-blocking mode"); | ||
| 312 | for(;;) { | ||
| 313 | if(select(MAX(ptm, STDIN_FILENO) + 1, &rfds, NULL, NULL, NULL) == -1) | ||
| 314 | err(EXIT_FAILURE, "cannot select"); | ||
| 315 | if(FD_ISSET(ptm, &rfds)) { | ||
| 316 | for(;;) { | ||
| 317 | if((c = getc(fptm)) == EOF) { | ||
| 318 | if(feof(fptm)) { | ||
| 319 | FD_CLR(ptm, &rfds); | ||
| 320 | fflush(fptm); | ||
| 321 | break; | ||
| 322 | } | ||
| 323 | if(errno != EAGAIN) | ||
| 324 | err(EXIT_FAILURE, "cannot read from pty"); | ||
| 325 | fflush(stdout); | ||
| 326 | break; | ||
| 327 | } | ||
| 328 | switch(c) { | ||
| 329 | case '\033': | ||
| 330 | parseesc(); | ||
| 331 | break; | ||
| 332 | default: | ||
| 333 | putchar(c); | ||
| 334 | } | ||
| 335 | } | ||
| 336 | fflush(stdout); | ||
| 337 | } | ||
| 338 | if(FD_ISSET(STDIN_FILENO, &rfds)) { | ||
| 339 | for(;;) { | ||
| 340 | if((c = getchar()) == EOF) { | ||
| 341 | if(feof(stdin)) { | ||
| 342 | FD_CLR(STDIN_FILENO, &rfds); | ||
| 343 | fflush(fptm); | ||
| 344 | break; | ||
| 345 | } | ||
| 346 | if(errno != EAGAIN) | ||
| 347 | err(EXIT_FAILURE, "cannot read from stdin"); | ||
| 348 | fflush(fptm); | ||
| 349 | break; | ||
| 350 | } | ||
| 351 | switch(c) { | ||
| 352 | case ':': | ||
| 353 | parsecmd(); | ||
| 354 | break; | ||
| 355 | default: | ||
| 356 | putc(c, fptm); | ||
| 357 | } | ||
| 358 | } | ||
| 359 | fflush(fptm); | ||
| 360 | } | ||
| 361 | } | ||
| 362 | return 0; | ||
| 363 | } | ||
