std.c 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  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. #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. static void buffer(char c);
  17. static void cmd(const char *cmdstr, ...);
  18. static int getch();
  19. void getpty(void);
  20. static void movea(int x, int y);
  21. static void mover(int x, int y);
  22. static void parseesc(void);
  23. static void scroll(int l);
  24. static void shell(void);
  25. static void sigchld(int n);
  26. static char unbuffer(void);
  27. static void ungetch(int c);
  28. typedef struct {
  29. unsigned char data[BUFSIZ];
  30. int s, e;
  31. int n;
  32. } RingBuffer;
  33. typedef struct {
  34. unsigned char data[BUFSIZ];
  35. int i, n;
  36. } ReadBuffer;
  37. static int cols = 80, lines = 25;
  38. static int cx = 0, cy = 0;
  39. static int c;
  40. int ptm, pts;
  41. static _Bool bold, digit, qmark;
  42. static pid_t pid;
  43. static RingBuffer buf;
  44. static ReadBuffer rbuf;
  45. void
  46. buffer(char c) {
  47. if(buf.n < LENGTH(buf.data))
  48. buf.n++;
  49. else
  50. buf.s = (buf.s + 1) % LENGTH(buf.data);
  51. buf.data[buf.e++] = c;
  52. buf.e %= LENGTH(buf.data);
  53. }
  54. void
  55. cmd(const char *cmdstr, ...) {
  56. va_list ap;
  57. putchar('\n');
  58. putchar(':');
  59. va_start(ap, cmdstr);
  60. vfprintf(stdout, cmdstr, ap);
  61. va_end(ap);
  62. }
  63. int
  64. getch() {
  65. if(rbuf.i++ >= rbuf.n) {
  66. rbuf.n = read(ptm, rbuf.data, LENGTH(rbuf.data));
  67. if(rbuf.n == -1)
  68. err(EXIT_FAILURE, "cannot read from slave pty");
  69. rbuf.i = 0;
  70. }
  71. return rbuf.data[rbuf.i];
  72. }
  73. void
  74. movea(int x, int y) {
  75. x = MAX(x, cols);
  76. y = MAX(y, lines);
  77. cx = x;
  78. cy = y;
  79. cmd("seek(%d,%d)", x, y);
  80. }
  81. void
  82. mover(int x, int y) {
  83. movea(cx + x, cy + y);
  84. }
  85. void
  86. parseesc(void) {
  87. int i, j;
  88. int arg[16];
  89. memset(arg, 0, LENGTH(arg));
  90. c = getch();
  91. switch(c) {
  92. case '[':
  93. c = getch();
  94. for(j = 0; j < LENGTH(arg);) {
  95. if(isdigit(c)) {
  96. digit = 1;
  97. arg[j] *= 10;
  98. arg[j] += c - '0';
  99. }
  100. else if(c == '?')
  101. qmark = 1;
  102. else if(c == ';') {
  103. if(!digit)
  104. errx(EXIT_FAILURE, "syntax error");
  105. digit = 0;
  106. j++;
  107. }
  108. else {
  109. if(digit) {
  110. digit = 0;
  111. j++;
  112. }
  113. break;
  114. }
  115. c = getch();
  116. }
  117. switch(c) {
  118. case '@':
  119. break;
  120. case 'A':
  121. mover(0, j ? arg[0] : 1);
  122. break;
  123. case 'B':
  124. mover(0, j ? -arg[0] : -1);
  125. break;
  126. case 'C':
  127. mover(j ? arg[0] : 1, 0);
  128. break;
  129. case 'D':
  130. mover(j ? -arg[0] : -1, 0);
  131. break;
  132. case 'E':
  133. /* movel(j ? arg[0] : 1); */
  134. break;
  135. case 'F':
  136. /* movel(j ? -arg[0] : -1); */
  137. break;
  138. case '`':
  139. case 'G':
  140. movea(j ? arg[0] : 1, cy);
  141. break;
  142. case 'f':
  143. case 'H':
  144. movea(arg[1] ? arg[1] : 1, arg[0] ? arg[0] : 1);
  145. case 'L':
  146. /* insline(j ? arg[0] : 1); */
  147. break;
  148. case 'M':
  149. /* delline(j ? arg[0] : 1); */
  150. break;
  151. case 'P':
  152. break;
  153. case 'S':
  154. scroll(j ? arg[0] : 1);
  155. break;
  156. case 'T':
  157. scroll(j ? -arg[0] : -1);
  158. break;
  159. case 'd':
  160. movea(cx, j ? arg[0] : 1);
  161. break;
  162. case 'm':
  163. for(i = 0; i < j; i++) {
  164. if(arg[i] >= 30 && arg[i] <= 37)
  165. cmd("#%d", arg[i] - 30);
  166. if(arg[i] >= 40 && arg[i] <= 47)
  167. cmd("|%d", arg[i] - 40);
  168. /* xterm bright colors */
  169. if(arg[i] >= 90 && arg[i] <= 97)
  170. cmd("#%d", arg[i] - 90);
  171. if(arg[i] >= 100 && arg[i] <= 107)
  172. cmd("|%d", arg[i] - 100);
  173. switch(arg[i]) {
  174. case 0:
  175. case 22:
  176. if(bold)
  177. cmd("bold");
  178. case 1:
  179. if(!bold)
  180. cmd("bold");
  181. break;
  182. }
  183. }
  184. break;
  185. }
  186. break;
  187. default:
  188. putchar('\033');
  189. ungetch(c);
  190. }
  191. }
  192. void
  193. scroll(int l) {
  194. cmd("seek(%d,%d)", cx, cy + l);
  195. }
  196. void
  197. shell(void) {
  198. static char *shell = NULL;
  199. if(!shell && !(shell = getenv("SHELL")))
  200. shell = "/bin/sh";
  201. pid = fork();
  202. switch(pid) {
  203. case -1:
  204. err(EXIT_FAILURE, "cannot fork");
  205. case 0:
  206. setsid();
  207. dup2(pts, STDIN_FILENO);
  208. dup2(pts, STDOUT_FILENO);
  209. dup2(pts, STDERR_FILENO);
  210. close(ptm);
  211. putenv("TERM=vt102");
  212. execvp(shell, NULL);
  213. break;
  214. default:
  215. close(pts);
  216. signal(SIGCHLD, sigchld);
  217. }
  218. }
  219. void
  220. sigchld(int n) {
  221. int ret;
  222. if(waitpid(pid, &ret, 0) == -1)
  223. err(EXIT_FAILURE, "waiting for child failed");
  224. if(WIFEXITED(ret))
  225. exit(WEXITSTATUS(ret));
  226. else
  227. exit(EXIT_SUCCESS);
  228. }
  229. char
  230. unbuffer(void) {
  231. char c;
  232. c = buf.data[buf.s++];
  233. buf.s %= LENGTH(buf.data);
  234. buf.n--;
  235. return c;
  236. }
  237. void
  238. ungetch(int c) {
  239. if(rbuf.i + 1 >= rbuf.n)
  240. errx(EXIT_FAILURE, "read buffer full");
  241. rbuf.data[rbuf.i++] = c;
  242. }
  243. int
  244. main(int argc, char *argv[]) {
  245. fd_set rfds;
  246. int r;
  247. if(argc == 2 && !strcmp("-v", argv[1])) {
  248. fprintf(stderr, "std-"VERSION", © 2008 Matthias-Christian Ott\n");
  249. exit(EXIT_SUCCESS);
  250. }
  251. else if(argc == 1) {
  252. fprintf(stderr, "usage: st [-v]\n");
  253. exit(EXIT_FAILURE);
  254. }
  255. getpty();
  256. shell();
  257. FD_ZERO(&rfds);
  258. FD_SET(STDIN_FILENO, &rfds);
  259. FD_SET(ptm, &rfds);
  260. for(;;) {
  261. r = select(ptm + 1, &rfds, NULL, NULL, NULL);
  262. if(r == -1)
  263. err(EXIT_FAILURE, "cannot select");
  264. if(FD_ISSET(ptm, &rfds)) {
  265. do {
  266. c = getch();
  267. switch(c) {
  268. case '\033':
  269. parseesc();
  270. break;
  271. default:
  272. putchar(c);
  273. }
  274. } while(rbuf.i < rbuf.n);
  275. fflush(stdout);
  276. }
  277. }
  278. return 0;
  279. }