term.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482
  1. #include <stdlib.h>
  2. #include <string.h>
  3. #include <stdio.h>
  4. #include "term.h"
  5. #define MAX_PARAMS 2
  6. static char *colors[8] = {
  7. "000000",
  8. "ff0000",
  9. "00ff00",
  10. "ffff00",
  11. "0000ff",
  12. "ff00ff",
  13. "00ffff",
  14. "ffffff",
  15. };
  16. void term_init(struct term *t, int num_rows, int num_cols, int pango)
  17. {
  18. t->state = TERM_STATE_GROUND;
  19. t->cursor.x = 0;
  20. t->cursor.y = 0;
  21. t->num_rows = num_rows;
  22. t->num_cols = num_cols;
  23. t->rows = calloc(t->num_rows, sizeof(struct row));
  24. t->param_idx = 0;
  25. t->params[0] = 0;
  26. t->params[1] = 0;
  27. t->pango = pango;
  28. t->sgra.normal = 1;
  29. for (int i = 0; i < t->num_rows; i++) {
  30. t->rows[i].cells = calloc(t->num_cols, sizeof(struct cell));
  31. for (int j = 0; j < t->num_cols; j++) {
  32. struct cell *cur_cell = &t->rows[i].cells[j];
  33. cur_cell->c = ' ';
  34. cur_cell->sgra.normal = 1;
  35. }
  36. }
  37. }
  38. static void term_cursor_move_up(struct term *t, int off) {
  39. t->cursor.y -= off;
  40. if (t->cursor.y < 0) {
  41. t->cursor.y = 0;
  42. }
  43. }
  44. static void term_cursor_move_left(struct term *t, int off) {
  45. t->cursor.x -= off;
  46. if (t->cursor.x < 0) {
  47. t->cursor.x = 0;
  48. }
  49. }
  50. static void term_cursor_move_down(struct term *t, int off) {
  51. t->cursor.y += off;
  52. if (t->cursor.y > t->num_rows - 1) {
  53. t->cursor.y = t->num_rows - 1;
  54. }
  55. }
  56. static void term_cursor_move_right(struct term *t, int off) {
  57. t->cursor.x += off;
  58. if (t->cursor.x > t->num_cols - 1) {
  59. t->cursor.x = t->num_cols - 1;
  60. }
  61. }
  62. static void term_line_feed(struct term *t)
  63. {
  64. if (t->cursor.y == t->num_rows - 1) {
  65. for (int i = 1; i < t->num_rows; i++) {
  66. memcpy(t->rows[i-1].cells, t->rows[i].cells, t->num_cols * sizeof(struct cell));
  67. }
  68. for (int i = 0; i < t->num_cols; i++) {
  69. t->rows[t->cursor.y].cells[i].c = ' ';
  70. }
  71. } else {
  72. t->cursor.y++;
  73. }
  74. }
  75. static void term_print(struct term *t, char c) {
  76. struct row *cur_row = &t->rows[t->cursor.y];
  77. struct cell *cur_cell = &cur_row->cells[t->cursor.x];
  78. cur_cell->c = c;
  79. cur_cell->sgra = t->sgra;
  80. if (t->cursor.x >= t->num_cols - 1) {
  81. term_line_feed(t);
  82. t->cursor.x = 0;
  83. } else {
  84. t->cursor.x++;
  85. }
  86. }
  87. static void term_clear(struct term *t) {
  88. t->param_idx = 0;
  89. t->params[0] = 0;
  90. t->params[1] = 0;
  91. // TODO: also clear:
  92. // private flag?
  93. // intermediate characters?
  94. // final character?
  95. }
  96. static void term_add_param_digit(struct term *t, char c) {
  97. t->params[t->param_idx] *= 10;
  98. t->params[t->param_idx] += c - '0';
  99. }
  100. static void term_next_param(struct term *t) {
  101. if (t->param_idx > TERM_MAX_PARAMS - 1)
  102. return;
  103. t->params[++t->param_idx] = 0;
  104. }
  105. static void term_param(struct term *t, char c) {
  106. switch (c) {
  107. case 0x30 ... 0x39: term_add_param_digit(t, c); break;
  108. case 0x3b: term_next_param(t); break;
  109. }
  110. }
  111. static void term_execute(struct term *t, char c) {
  112. switch (c) {
  113. case '\b': term_cursor_move_left(t, 1); break;
  114. case '\f':
  115. case '\n':
  116. case '\v':
  117. term_line_feed(t); break;
  118. case '\r': t->cursor.x = 0; break;
  119. case '\t':
  120. term_print(t, ' ');
  121. term_print(t, ' ');
  122. term_print(t, ' ');
  123. term_print(t, ' ');
  124. break;
  125. }
  126. }
  127. static void term_collect(struct term *t, char c) {
  128. // not implemented
  129. }
  130. static void term_csi_erase_in_display(struct term *t) {
  131. // inclusive
  132. int start_x;
  133. int start_y;
  134. // exclusive
  135. int end_x;
  136. int end_y;
  137. switch (t->params[0]) {
  138. case 0:
  139. start_x = t->cursor.x;
  140. start_y = t->cursor.y;
  141. end_x = t->num_cols;
  142. end_y = t->num_rows;
  143. break;
  144. case 1:
  145. start_x = 0;
  146. start_y = 0;
  147. end_x = t->cursor.x + 1;
  148. end_y = t->cursor.y + 1;
  149. break;
  150. // We don't have a scrollback buffer, so 2 and 3 are equivalent
  151. case 2:
  152. case 3:
  153. start_x = 0;
  154. start_y = 0;
  155. end_x = t->num_cols;
  156. end_y = t->num_rows;
  157. break;
  158. default: return;
  159. }
  160. for (int i = start_y; i < end_y; i++) {
  161. for (int j = start_x; j < end_x; j++) {
  162. struct cell *cur_cell = &t->rows[i].cells[j];
  163. cur_cell->c = ' ';
  164. cur_cell->sgra.normal = 1;
  165. }
  166. }
  167. }
  168. static void term_csi_erase_in_line(struct term *t) {
  169. int start_x; // inclusive
  170. int end_x; // exclusive
  171. switch (t->params[0]) {
  172. case 0:
  173. start_x = t->cursor.x;
  174. end_x = t->num_cols;
  175. break;
  176. case 1:
  177. start_x = 0;
  178. end_x = t->cursor.x + 1;
  179. break;
  180. case 2:
  181. start_x = 0;
  182. end_x = t->num_cols;
  183. break;
  184. }
  185. for (int j = start_x; j < end_x; j++) {
  186. struct cell *cur_cell = &t->rows[t->cursor.y].cells[j];
  187. cur_cell->c = ' ';
  188. cur_cell->sgra.normal = 1;
  189. }
  190. }
  191. static void term_csi_sgr(struct term *t) {
  192. for (int i = 0; i < t->param_idx + 1; i++) {
  193. int p = t->params[i];
  194. if (0 == p) {
  195. t->sgra.normal = 1;
  196. } else {
  197. t->sgra.normal = 0;
  198. switch (p) {
  199. case 1: t->sgra.bold = 1; break;
  200. case 30 ... 37: t->sgra.fg = p - 30; break;
  201. case 40 ... 47: t->sgra.bg = p - 40; break;
  202. }
  203. }
  204. }
  205. }
  206. static void term_csi_dispatch(struct term *t, char c) {
  207. int p0 = t->params[0];
  208. int p1 = t->params[1];
  209. switch (c) {
  210. case 'A': term_cursor_move_up(t, p0 == 0 ? 1 : p0); break;
  211. case 'B': term_cursor_move_down(t, p0 == 0 ? 1 : p0); break;
  212. case 'C': term_cursor_move_right(t, p0 == 0 ? 1 : p0); break;
  213. case 'D': term_cursor_move_left(t, p0 == 0 ? 1 : p0); break;
  214. case 'E': t->cursor.x = 0; term_cursor_move_down(t, p0 == 0 ? 1 : p0); break;
  215. case 'F': t->cursor.x = 0; term_cursor_move_up(t, p0 == 0 ? 1 : p0); break;
  216. case 'G': t->cursor.x = p0; break;
  217. case 'H': t->cursor.y = p0 == 0 ? 0 : p0 - 1; t->cursor.x = p1 == 0 ? 0 : p1 - 1; break;
  218. case 'J': term_csi_erase_in_display(t); break;
  219. case 'K': term_csi_erase_in_line(t); break;
  220. case 'm': term_csi_sgr(t); break;
  221. }
  222. }
  223. static void term_esc_dispatch(struct term *t, char c) {
  224. switch (c) {
  225. case 'D': term_line_feed(t); break;
  226. case 'E': t->cursor.x = 0; term_line_feed(t); break;
  227. }
  228. }
  229. void term_putc(struct term *t, char c)
  230. {
  231. // Loosely based on a very (very) limited subset of the state machine at https://vt100.net/emu/dec_ansi_parser
  232. // Expect most things to be unsupported, irrelevant or straight out broken ^__-
  233. switch (t->state) {
  234. case TERM_STATE_GROUND:
  235. // events
  236. switch (c) {
  237. case 0x00 ... 0x17:
  238. case 0x19:
  239. case 0x1c ... 0x1f: term_execute(t, c); break;
  240. case 0x1b: t->state = TERM_STATE_ESC; break;
  241. case 0x20 ... 0x7f: term_print(t, c); break;
  242. } break;
  243. case TERM_STATE_ESC:
  244. // entry
  245. term_clear(t);
  246. // events
  247. switch (c) {
  248. case 0x00 ... 0x17:
  249. case 0x19:
  250. case 0x1c ... 0x1f: term_execute(t, c); break;
  251. case 0x20 ... 0x2f: term_collect(t, c); t->state = TERM_STATE_ESC_INTERMEDIATE; break;
  252. case 0x30 ... 0x4f:
  253. case 0x51 ... 0x57:
  254. case 0x59:
  255. case 0x5a:
  256. case 0x5c:
  257. case 0x60 ... 0x7e: term_esc_dispatch(t, c); t->state = TERM_STATE_GROUND; break;
  258. case 0x5b: t->state = TERM_STATE_CSI_ENTRY; break;
  259. } break;
  260. case TERM_STATE_ESC_INTERMEDIATE:
  261. switch (c) {
  262. case 0x00 ... 0x17:
  263. case 0x19:
  264. case 0x1c ... 0x1f: term_execute(t, c); break;
  265. case 0x1b: t->state = TERM_STATE_ESC; break;
  266. case 0x20 ... 0x2f: term_collect(t, c); break;
  267. case 0x30 ... 0x7e: term_esc_dispatch(t, c); t->state = TERM_STATE_GROUND; break;
  268. } break;
  269. case TERM_STATE_CSI_ENTRY:
  270. // entry
  271. term_clear(t);
  272. // events
  273. switch (c) {
  274. case 0x00 ... 0x17:
  275. case 0x19:
  276. case 0x1c ... 0x1f: term_execute(t, c); break;
  277. case 0x1b: t->state = TERM_STATE_ESC; break;
  278. case 0x20 ... 0x2f: term_collect(t, c); t->state = TERM_STATE_CSI_INTERMEDIATE; break;
  279. case 0x3a: t->state = TERM_STATE_CSI_IGNORE; break;
  280. case 0x3c ... 0x3f: term_collect(t, c); break;
  281. case 0x30 ... 0x39:
  282. case 0x3b:
  283. term_param(t, c); t->state = TERM_STATE_CSI_PARAM; break;
  284. case 0x40 ... 0x7e: term_csi_dispatch(t, c); t->state = TERM_STATE_GROUND; break;
  285. } break;
  286. case TERM_STATE_CSI_PARAM:
  287. // events
  288. switch (c) {
  289. case 0x00 ... 0x17:
  290. case 0x19:
  291. case 0x1c ... 0x1f: term_execute(t, c); break;
  292. case 0x1b: t->state = TERM_STATE_ESC; break;
  293. case 0x20 ... 0x2f: term_collect(t, c); t->state = TERM_STATE_CSI_INTERMEDIATE; break;
  294. case 0x30 ... 0x39:
  295. case 0x3b:
  296. term_param(t, c); break;
  297. case 0x3a:
  298. case 0x3c ... 0x3f: t->state = TERM_STATE_CSI_IGNORE; break;
  299. case 0x40 ... 0x7e: term_csi_dispatch(t, c); t->state = TERM_STATE_GROUND; break;
  300. } break;
  301. case TERM_STATE_CSI_INTERMEDIATE:
  302. // events
  303. switch (c) {
  304. case 0x00 ... 0x17:
  305. case 0x19:
  306. case 0x1c ... 0x1f: term_execute(t, c); break;
  307. case 0x1b: t->state = TERM_STATE_ESC; break;
  308. case 0x20 ... 0x2f: term_collect(t, c); break;
  309. case 0x30 ... 0x3f: t->state = TERM_STATE_CSI_IGNORE; break;
  310. case 0x40 ... 0x7e: term_csi_dispatch(t, c); t->state = TERM_STATE_GROUND; break;
  311. } break;
  312. case TERM_STATE_CSI_IGNORE:
  313. // events
  314. switch (c) {
  315. case 0x00 ... 0x17:
  316. case 0x19:
  317. case 0x1c ... 0x1f: term_execute(t, c); break;
  318. case 0x1b: t->state = TERM_STATE_ESC; break;
  319. case 0x40 ... 0x7e: t->state = TERM_STATE_GROUND; break;
  320. } break;
  321. }
  322. }
  323. void term_put_string(struct term *t, char *str) {
  324. int len = strlen(str);
  325. for (int i = 0; i < len; i++) {
  326. term_putc(t, str[i]);
  327. }
  328. }
  329. static char *xml_entity(char *str, char c) {
  330. switch (c) {
  331. case '<':
  332. strncpy(str, "&#x003C;", 9);
  333. break;
  334. case '>':
  335. strncpy(str, "&#x003E;", 9);
  336. break;
  337. default:
  338. str[0] = c;
  339. str[1] = '\0';
  340. break;
  341. }
  342. return str;
  343. }
  344. char *term_to_string(struct term *t)
  345. {
  346. // TODO: Allocate once
  347. // TODO: don't allocate too much if pango is not used.
  348. int str_size = t->num_rows * t->num_cols * 128 + t->num_rows;
  349. char *ret = calloc(str_size, sizeof(char));
  350. char *s = ret;
  351. char cell_str[9];
  352. for (int i = 0; i < t->num_rows; i++) {
  353. for (int j = 0; j < t->num_cols; j++) {
  354. struct cell cur_cell = t->rows[i].cells[j];
  355. if (t->pango && 0 == cur_cell.sgra.normal) {
  356. // TODO: use snprintf...
  357. s += sprintf(s, "<span weight='%s' fgcolor='#%s' bgcolor='#%s'>%s</span>",
  358. cur_cell.sgra.bold ? "bold" : "normal",
  359. colors[cur_cell.sgra.fg],
  360. colors[cur_cell.sgra.bg],
  361. xml_entity(cell_str, cur_cell.c));
  362. } else {
  363. *s++ = cur_cell.c;
  364. }
  365. }
  366. if (i == t->num_rows - 1) {
  367. *s++ = '\0';
  368. } else {
  369. *s++ = '\n';
  370. }
  371. }
  372. return ret;
  373. }
  374. void term_free(struct term *t)
  375. {
  376. if (t->rows != NULL) {
  377. for(int i = 0; i < t->num_rows; i++) {
  378. if (t->rows[i].cells != NULL) {
  379. free(t->rows[i].cells);
  380. }
  381. }
  382. free(t->rows);
  383. }
  384. }