Simple Terminal from SuckLess
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

332 lines
5.6 KiB

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