std.c 6.7 KB

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