std.c 6.9 KB

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