Merge pull request #13 from rdebath/patch-fixes
[congif.git] / term.c
blob92682780e60b7b9eacced8647fa7dd64ecf1e342
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <stdint.h>
4 #include <string.h>
5 #include <stdarg.h>
7 #include "term.h"
8 #include "default.h"
9 #include "cs_vtg.h"
10 #include "cs_437.h"
12 #define MAX(A, B) ((A) > (B) ? (A) : (B))
13 #define CLEARWRAP do{ if (term->col >= term->cols) term->col = term->cols-1; }while(0)
14 #define CLIPROW(X) if (term->row<0 || term->row >= term->rows) term->row = X
15 #define CLIPCOL(X) if (term->col<0 || term->col >= term->cols) term->col = X
17 static int verbose = 0;
19 static void
20 logfmt(char *fmt, ...)
22 va_list args;
23 va_start(args, fmt);
24 if (verbose)
25 vfprintf(stderr, fmt, args);
26 va_end(args);
29 void
30 set_verbosity(int level)
32 verbose = level;
35 static void
36 save_cursor(Term *term)
38 term->save_cursor.row = term->row;
39 term->save_cursor.col = term->col;
42 static void
43 load_cursor(Term *term)
45 term->row = term->save_cursor.row;
46 term->col = term->save_cursor.col;
49 static void
50 save_misc(Term *term)
52 term->save_misc.row = term->row;
53 term->save_misc.col = term->col;
54 term->save_misc.origin_on = term->mode & M_ORIGIN;
55 term->save_misc.attr = term->attr;
56 term->save_misc.pair = term->pair;
57 term->save_misc.cs_array[0] = term->cs_array[0];
58 term->save_misc.cs_array[1] = term->cs_array[1];
59 term->save_misc.cs_index = term->cs_index;
62 static void
63 load_misc(Term *term)
65 term->row = term->save_misc.row;
66 term->col = term->save_misc.col;
67 if (term->save_misc.origin_on)
68 term->mode |= M_ORIGIN;
69 else
70 term->mode &= ~M_ORIGIN;
71 term->attr = term->save_misc.attr;
72 term->pair = term->save_misc.pair;
73 term->cs_array[0] = term->save_misc.cs_array[0];
74 term->cs_array[1] = term->save_misc.cs_array[1];
75 term->cs_index = term->save_misc.cs_index;
78 void
79 set_default_palette(char * pname)
81 static struct {
82 char * name;
83 uint8_t * plt;
84 } pal[] = {
85 { "xterm", plt_xterm },
86 { "vga", plt_vga_ansi },
87 { "sol_ansi", plt_solarized_like_ansi },
88 { "solarized", plt_solarized },
89 { "putty", plt_putty },
90 { 0, 0}
93 if (pname[0] == '@') {
94 int i;
95 for(i=0; pal[i].name; i++) {
96 if (strcasecmp(pname+1, pal[i].name) == 0) {
97 def_plt = pal[i].plt;
98 return;
101 fprintf(stderr, "Known standard palette names are:\n");
102 for(i=0; pal[i].name; i++)
103 fprintf(stderr, " @%s\n", pal[i].name);
104 exit(2);
105 } else {
106 FILE * fd;
107 char buf[BUFSIZ];
108 Term * term = 0;
109 uint8_t * plt = malloc(sizeof(term->plt));
110 memcpy(plt, def_plt, sizeof(term->plt));
111 def_plt = plt;
113 if ((fd = fopen(pname, "r")) == 0) {
114 perror(pname); exit(1);
116 while (fgets(buf, sizeof(buf), fd) != 0) {
117 char * s = buf, *e;
118 long cno = 0, colour = 0;
120 while (*s == ' ' || *s == '\t') s++;
121 if (*s == '#') continue;
123 if (strncasecmp(s, "color", 5) == 0) {
124 s += 5;
125 } else if (strncasecmp(s, "colour", 6) == 0) {
126 s += 6;
127 } else continue;
129 // Only lines that match /^ *colou?r[0-9]+/i
130 if (*s < '0' || *s > '9') continue;
132 cno = strtol(s, &e, 0);
133 if (s == e) goto bad_line;
134 if (cno < 0 || cno > 16) goto bad_line;
136 s = e;
137 while (*s == ' ' || *s == '\t' || *s == '#' || *s == '=') s++;
138 colour = strtol(s, &e, 16);
139 if (e-s != 6) goto bad_line;
141 plt[cno*3+0] = ((colour>>16) & 0xFF);
142 plt[cno*3+1] = ((colour>> 8) & 0xFF);
143 plt[cno*3+2] = ((colour ) & 0xFF);
144 continue;
145 bad_line:
146 fprintf(stderr, "Bad line in colour file: %s", buf);
147 exit(2);
150 fclose(fd);
152 return;
155 static void
156 reset(Term *term)
158 int i, j;
160 term->row = term->col = 0;
161 term->top = 0;
162 term->bot = term->rows - 1;
163 term->mode = def_mode;
164 term->attr = def_attr;
165 term->pair = def_pair;
166 term->cs_array[0] = CS_BMP;
167 term->cs_array[1] = CS_VTG;
168 term->cs_index = 0;
169 term->state = S_ANY;
170 term->parlen = 0;
171 if (memcmp(term->plt, def_plt, sizeof(term->plt) != 0)) {
172 term->plt_dirty = 1;
174 memcpy(term->plt, def_plt, sizeof(term->plt));
175 term->plt_local = 0;
176 for (i = 0; i < term->rows; i++) {
177 term->addr[i] = &term->cells[i*term->cols];
178 for (j = 0; j < term->cols; j++)
179 term->addr[i][j] = (Cell) {EMPTY, def_attr, def_pair};
181 save_cursor(term);
182 save_misc(term);
185 Term *
186 new_term(int rows, int cols)
188 size_t size = sizeof(Term) + rows*sizeof(Cell *) + rows*cols*sizeof(Cell);
189 Term *term = malloc(size);
190 if (!term)
191 return NULL;
192 term->rows = rows;
193 term->cols = cols;
194 term->addr = (Cell **) &term[1];
195 term->cells = (Cell *) &term->addr[rows];
196 reset(term);
197 term->plt_dirty = 0;
198 return term;
201 static uint16_t
202 char_code(Term *term)
204 int i;
205 uint16_t code = term->partial[0] & ((1 << (8 - term->parlen)) - 1);
206 for (i = 1; i < term->parlen; i++)
207 code = (code << 6) | (term->partial[i] & 0x3F);
208 return code;
211 static int
212 within_bounds(Term *term, int row, int col)
214 if (row < 0 || row >= term->rows || col < 0 || col > term->cols) {
215 logfmt("position %d,%d is out of bounds %d,%d\n",
216 row+1, col+1, term->rows, term->cols);
217 return 0;
218 } else {
219 return 1;
223 /* Move lines down and put a blank line at the top. */
224 static void
225 scroll_up(Term *term)
227 int row, col;
228 Cell *addr;
230 if (!within_bounds(term, term->top, 0))
231 return;
232 if (!within_bounds(term, term->bot, 0))
233 return;
234 addr = term->addr[term->bot];
235 for (row = term->bot; row > term->top; row--)
236 term->addr[row] = term->addr[row-1];
237 term->addr[term->top] = addr;
238 for (col = 0; col < term->cols; col++)
239 term->addr[term->top][col] = BLANK;
242 /* Move lines up and put a blank line at the bottom. */
243 static void
244 scroll_down(Term *term)
246 int row, col;
247 Cell *addr;
249 if (!within_bounds(term, term->top, 0))
250 return;
251 if (!within_bounds(term, term->bot, 0))
252 return;
253 addr = term->addr[term->top];
254 for (row = term->top; row < term->bot; row++)
255 term->addr[row] = term->addr[row+1];
256 term->addr[term->bot] = addr;
257 for (col = 0; col < term->cols; col++)
258 term->addr[term->bot][col] = BLANK;
261 static void
262 addchar(Term *term, uint16_t code)
264 Cell cell = (Cell) {code, term->attr, term->pair};
265 if (term->col >= term->cols) {
266 if (term->mode & M_AUTOWRAP) {
267 term->col = 0;
268 if (term->row < term->bot)
269 term->row++;
270 else
271 scroll_down(term);
272 } else {
273 term->col = term->cols - 1;
276 if (!within_bounds(term, term->row, term->col))
277 return;
278 if (term->mode & M_INSERT) {
279 Cell next;
280 int col;
281 for (col = term->col; col < term->cols; col++) {
282 next = term->addr[term->row][col];
283 term->addr[term->row][col] = cell;
284 cell = next;
286 } else {
287 term->addr[term->row][term->col] = cell;
289 term->col++;
292 static void
293 linefeed(Term *term)
295 if (term->row == term->bot)
296 scroll_down(term);
297 else
298 term->row++;
299 if (term->mode & M_NEWLINE)
300 term->col = 0;
303 static void
304 ctrlchar(Term *term, uint8_t byte)
306 switch (byte) {
307 case 0x08:
308 CLEARWRAP;
309 if (term->col) term->col--;
310 break;
311 case 0x09:
312 /* TODO: See ESC Sequence H (HTS) */
313 term->col &= ~7; term->col += 8;
314 CLIPCOL(term->cols-1);
315 break;
316 case 0x0A: case 0x0B: case 0x0C:
317 CLEARWRAP;
318 linefeed(term);
319 break;
320 case 0x0D:
321 term->col = 0;
322 break;
323 case 0x0E:
324 term->cs_index = 1;
325 break;
326 case 0x0F:
327 term->cs_index = 0;
328 break;
332 static void
333 escseq(Term *term, uint8_t byte)
335 uint8_t first, second;
337 if (term->parlen) {
338 first = *term->partial;
339 second = byte;
340 } else {
341 first = byte;
342 second = 0;
344 switch (first) {
345 case 'c':
346 reset(term);
347 break;
348 case 'D':
349 CLEARWRAP;
350 if (term->row == term->bot)
351 scroll_down(term);
352 else
353 term->row++;
354 break;
355 case 'E':
356 CLEARWRAP;
357 if (term->row == term->bot) {
358 scroll_down(term);
359 term->col = 0;
360 } else {
361 term->row++;
363 break;
364 case 'H':
365 /* TODO: set tab stop at current column */
366 logfmt("NYI: ESC Sequence H (HTS)\n");
367 break;
368 case 'M':
369 if (term->row == term->top)
370 scroll_up(term);
371 else
372 term->row--;
373 break;
374 case 'Z':
375 /* Identify Terminal (DECID) */
376 /* if we were a real terminal, we'd reply with "ESC [ ? 6 c" */
377 /* since there is no application listening, we can ignore this */
378 break;
379 case '7':
380 CLEARWRAP;
381 save_misc(term);
382 break;
383 case '8':
384 CLEARWRAP;
385 load_misc(term);
386 break;
387 case '%':
388 /* TODO: select charset */
389 logfmt("NYI: ESC Sequence %% (character set selection)\n");
390 switch(second)
392 case '8': /* Linux switch to UTF8 */
393 case 'G': /* DOCS: Designate other coding system */
394 term->mode &= ~M_ISOLAT1;
395 if (term->cs_array[0] == CS_ISO)
396 term->cs_array[0] = CS_BMP;
397 if (term->cs_array[1] == CS_ISO)
398 term->cs_array[1] = CS_BMP;
399 break;
400 case '@': /* DOCS, Standard return */
401 term->mode |= M_ISOLAT1;
402 if (term->cs_array[0] == CS_BMP)
403 term->cs_array[0] = CS_ISO;
404 if (term->cs_array[1] == CS_BMP)
405 term->cs_array[1] = CS_ISO;
406 break;
408 break;
409 case '#':
410 switch(second)
412 case '8':
414 int i, j;
415 for (i = 0; i < term->rows; i++) {
416 for (j = 0; j < term->cols; j++) {
417 term->addr[i][j] = (Cell) {'E', def_attr, def_pair};
421 break;
422 default:
423 /* TODO */
424 logfmt("NYI: ESC Sequence # 3..6 DECDWL etc\n");
425 break;
427 break;
428 case '(':
429 switch (second) {
430 case 'B':
431 term->cs_array[0] = (term->mode&M_ISOLAT1)?CS_ISO:CS_BMP;
432 break;
433 case '0':
434 term->cs_array[0] = CS_VTG;
435 break;
436 case 'U':
437 term->cs_array[0] = CS_437;
438 break;
439 case 'K':
440 logfmt("UNS: user-defined mapping\n");
441 term->cs_array[0] = (term->mode&M_ISOLAT1)?CS_ISO:CS_BMP;
442 break;
444 break;
445 case ')':
446 switch (second) {
447 case 'B':
448 term->cs_array[1] = (term->mode&M_ISOLAT1)?CS_ISO:CS_BMP;
449 break;
450 case '0':
451 term->cs_array[1] = CS_VTG;
452 break;
453 case 'U':
454 term->cs_array[1] = CS_437;
455 break;
456 case 'K':
457 logfmt("UNS: user-defined mapping\n");
458 term->cs_array[1] = (term->mode&M_ISOLAT1)?CS_ISO:CS_BMP;
459 break;
461 break;
462 case '>':
463 /* TODO: set numeric keypad mode */
464 logfmt("NYI: ESC Sequence > (DECPNM)\n");
465 break;
466 case '=':
467 /* TODO: set application keypad mode */
468 logfmt("NYI: ESC Sequence = (DECPAM)\n");
469 break;
470 default:
471 logfmt("UNS: ESC Sequence %c\n", first);
475 static int
476 do_linux_osc(Term *term)
478 int i;
479 uint8_t buf[4] = {0,0,0,0};
480 if (term->partial[0] == 'R')
482 if (memcmp(term->plt, def_plt, sizeof(term->plt) != 0))
483 term->plt_dirty = 1;
484 memcpy(term->plt, def_plt, sizeof(term->plt));
485 term->plt_local = 0;
486 return 1;
488 if (term->partial[0] != 'P' || term->parlen != 8)
489 return 0;
491 for (i=1; i<8; i++) {
492 /* isxdigit is locale broken */
493 int ch = term->partial[i];
494 if (ch >= '0' && ch <= '9')
495 buf[i>>1] = (buf[i>>1] << 4) + (ch - '0');
496 else if (ch >= 'a' && ch <= 'f')
497 buf[i>>1] = (buf[i>>1] << 4) + (ch - 'a' + 10);
498 else if (ch >= 'A' && ch <= 'F')
499 buf[i>>1] = (buf[i>>1] << 4) + (ch - 'A' + 10);
500 else
501 return 0;
503 for(i=0; i<3; i++) {
504 if (term->plt[buf[0]*3+i] != buf[i+1]) {
505 term->plt[buf[0]*3+i] = buf[i+1];
506 term->plt_local = term->plt_dirty = 1;
510 return 1;
513 static int
514 getparams(char *partial, int *params, int n)
516 char *next;
517 char *token = partial;
518 int i = 0;
519 if (!*partial) {
520 params[0] = 0;
521 return 1;
523 while (i < n && *token) {
524 params[i++] = strtol(token, &next, 10);
525 if (*next) next++;
526 token = next;
528 if (i < n && *(token-1) == ';')
529 params[i++] = 0;
530 return i;
533 #define SWITCH(T, F, V) (T)->mode = (V) ? (T)->mode | (F) : (T)->mode & ~(F)
535 static void
536 modeswitch(Term *term, int private, int number, int value)
538 if (private) {
539 /* DEC modes */
540 switch (number) {
541 case 1:
542 SWITCH(term, M_CURSORKEY, value);
543 break;
544 case 3:
545 /* TODO: 80/132 columns mode switch */
546 logfmt("NYI: DEC mode 3\n");
547 break;
548 case 5:
549 SWITCH(term, M_REVERSE, value);
550 break;
551 case 6:
552 SWITCH(term, M_ORIGIN, value);
553 term->row = term->top;
554 term->col = 0;
555 break;
556 case 7:
557 SWITCH(term, M_AUTOWRAP, value);
558 break;
559 case 8:
560 SWITCH(term, M_AUTORPT, value);
561 break;
562 case 9:
563 SWITCH(term, M_MOUSEX10, value);
564 break;
565 case 25:
566 SWITCH(term, M_CURSORVIS, value);
567 break;
568 case 1000:
569 SWITCH(term, M_MOUSEX11, value);
570 break;
571 default:
572 logfmt("UNS: DEC mode %d\n", number);
574 } else {
575 /* ANSI modes */
576 switch (number) {
577 case 3:
578 SWITCH(term, M_DISPCTRL, value);
579 break;
580 case 4:
581 SWITCH(term, M_INSERT, value);
582 break;
583 case 20:
584 SWITCH(term, M_NEWLINE, value);
585 break;
586 default:
587 logfmt("UNS: ANSI mode %d\n", number);
592 static int
593 fake_colour_16m(int red, int green, int blue)
595 int av = (red+green+blue)/3;
596 int palno = 1*(red>=av) + 2*(green>=av) + 4*(blue>=av);
597 if (red==green && green==blue) {
598 if (av > 212) palno = 15-8;
599 else if (av > 127) palno = 7-8;
600 else if (av > 42) palno = 8;
601 else palno = 0;
603 return palno + 8*(av>127);
606 static int
607 fake_colour_256(int colnum){
608 int nr, ng, nb;
609 if (colnum < 16) {
610 nr = (colnum&1) ? 170:0;
611 ng = (colnum&2) ? 170:0;
612 nb = (colnum&4) ? 170:0;
613 if (colnum&8) { nr += 85; ng += 85; nb += 85; }
614 if (colnum == 3) ng = 85;
615 } else if (colnum < 232) {
616 int i = colnum - 16;
617 nr = i / 36; ng = (i / 6) % 6; nb = i % 6;
618 nr = nr ? nr * 40 + 55 : 0;
619 ng = ng ? ng * 40 + 55 : 0;
620 nb = nb ? nb * 40 + 55 : 0;
621 } else if (colnum < 256) {
622 int i = colnum - 232;
623 nr = ng = nb = i * 10 + 8;
624 } else
625 nr=ng=nb = 127;
627 return fake_colour_16m(nr, ng, nb);
630 static void
631 sgr(Term *term, int n, int *params)
633 int i, number;
634 for(i=0; i<n; i++) {
635 number = params[i];
637 switch (number) {
638 case 0:
639 term->attr = def_attr;
640 term->pair = def_pair;
641 break;
642 case 1:
643 term->attr |= A_BOLD;
644 break;
645 case 2:
646 term->attr |= A_DIM;
647 break;
648 case 3:
649 term->attr |= A_ITALIC;
650 break;
651 case 4:
652 term->attr |= A_UNDERLINE;
653 break;
654 case 5:
655 term->attr |= A_BLINK;
656 break;
657 case 7:
658 term->attr |= A_INVERSE;
659 break;
660 case 8:
661 term->attr |= A_INVISIBLE;
662 break;
663 case 10:
664 /* TODO: reset toggle meta flag */
665 term->cs_array[term->cs_index = 0] = (term->mode&M_ISOLAT1)?CS_ISO:CS_BMP;
666 term->mode &= ~M_DISPCTRL;
667 break;
668 case 11:
669 /* TODO: reset toggle meta flag */
670 term->cs_array[term->cs_index] = CS_437;
671 term->mode |= M_DISPCTRL;
672 break;
673 case 12:
674 /* TODO: set toggle meta flag */
675 term->cs_array[term->cs_index] = CS_437;
676 term->mode |= M_DISPCTRL;
677 break;
678 case 21:
679 #ifndef OLDLINUX
680 term->attr |= A_UNDERLINE; /* Linux say should be: DOUBLE_ULINE */
681 break;
682 #endif
683 case 22:
684 term->attr &= ~A_DIM;
685 term->attr &= ~A_BOLD;
686 break;
687 case 23:
688 term->attr &= ~A_ITALIC;
689 break;
690 case 24:
691 term->attr &= ~A_UNDERLINE;
692 break;
693 case 25:
694 term->attr &= ~A_BLINK;
695 break;
696 case 27:
697 term->attr &= ~A_INVERSE;
698 break;
699 case 30: case 31: case 32: case 33: case 34: case 35: case 36: case 37:
700 term->pair = ((number - 30) << 4) | (term->pair & 0x0F);
701 break;
702 #ifdef OLDLINUX
703 case 38:
704 term->attr |= A_UNDERLINE;
705 term->pair = (DEF_FORE << 4) | (term->pair & 0x0F);
706 break;
707 case 39:
708 term->attr &= ~A_UNDERLINE;
709 term->pair = (DEF_FORE << 4) | (term->pair & 0x0F);
710 break;
711 #endif
712 #ifndef OLDLINUX
713 case 38:
714 i++ ;
715 if (i>n) break;
716 if (params[i] == 5) {
717 /* 256 colours */
718 term->pair = (fake_colour_256(params[i+1]) << 4) | (term->pair & 0x0F);
719 i++;
720 break;
721 } else if (params[i] == 2) {
722 /* 16M colours, note I'm using common form not strict ITU T.416 */
723 term->pair = (fake_colour_16m(params[i+1],params[i+2],params[i+3]) << 4) | (term->pair & 0x0F);
724 i+=3;
725 break;
727 /* Could be CMYK; probably broken */
728 i = n;
729 break;
730 case 39:
731 term->pair = (DEF_FORE << 4) | (term->pair & 0x0F);
732 break;
733 #endif
734 case 40: case 41: case 42: case 43: case 44: case 45: case 46: case 47:
735 term->pair = (term->pair & 0xF0) | (number - 40);
736 break;
737 #ifndef OLDLINUX
738 case 48:
739 i++ ;
740 if (i>n) break;
741 if (params[i] == 5) {
742 /* 256 colours */
743 term->pair = (term->pair & 0xF0) | fake_colour_256(params[i+1]);
744 i++;
745 break;
746 } else if (params[i] == 2) {
747 /* 16M colours, note I'm using common form not strict ITU T.416 */
748 term->pair = (term->pair & 0xF0) | fake_colour_16m(params[i+1],params[i+2],params[i+3]);
749 i+=3;
750 break;
752 /* Could be CMYK; probably broken */
753 i = n;
754 break;
755 #endif
756 case 49:
757 term->pair = (term->pair & 0xF0) | DEF_BACK;
758 break;
759 case 90: case 91: case 92: case 93: case 94: case 95: case 96: case 97:
760 term->pair = ((number - 90 + 8) << 4) | (term->pair & 0x0F);
761 break;
762 case 100: case 101: case 102: case 103: case 104: case 105: case 106: case 107:
763 term->pair = (term->pair & 0xF0) | (number - 100 + 8);
764 break;
765 default:
766 logfmt("UNS: SGR %d\n", number);
771 static void
772 ctrlseq(Term *term, uint8_t byte)
774 int private;
775 int n, k, k1;
776 int params[MAX_PARAMS];
777 char *str;
778 int ra, rb, ca, cb;
779 int i, j;
780 Cell cell;
782 if (!within_bounds(term, term->row, term->col))
783 return;
784 term->partial[term->parlen] = '\0';
785 if (*term->partial == '?') {
786 private = 1;
787 str = (char *) term->partial + 1;
788 } else {
789 private = 0;
790 str = (char *) term->partial;
792 n = getparams(str, params, MAX_PARAMS);
793 k = n ? *params : 0;
794 k1 = k ? k : 1;
795 switch (byte) {
796 case '@':
797 CLEARWRAP;
798 /* TODO: insert the indicated # of blank characters */
799 logfmt("NYI: Control Sequence @ (ICH)\n");
800 break;
801 case 'A':
802 CLEARWRAP;
803 term->row -= k1;
804 CLIPROW(0);
805 break;
806 case 'B': case 'e':
807 CLEARWRAP;
808 term->row += k1;
809 CLIPROW(term->rows-1);
810 break;
811 case 'C': case 'a':
812 CLEARWRAP;
813 term->col += k1;
814 CLIPCOL(term->cols-1);
815 break;
816 case 'D':
817 CLEARWRAP;
818 term->col -= k1;
819 CLIPCOL(0);
820 break;
821 case 'E':
822 term->row += k1;
823 term->col = 0;
824 CLIPROW(term->rows-1);
825 break;
826 case 'F':
827 term->row -= k1;
828 term->col = 0;
829 CLIPROW(0);
830 break;
831 case 'G': case '`':
832 term->col = k1 - 1;
833 CLIPCOL(term->cols-1);
834 break;
835 case 'H': case 'f':
836 if (n == 2) {
837 term->row = MAX(params[0], 1) - 1;
838 term->col = MAX(params[1], 1) - 1;
839 } else if (n == 1) {
840 term->row = MAX(params[0], 1) - 1;
841 term->col = 0;
842 } else {
843 term->row = term->col = 0;
845 if (term->mode & M_ORIGIN)
846 term->row += term->top;
847 CLIPROW(term->rows-1);
848 CLIPCOL(term->cols-1);
849 break;
850 case 'J':
851 CLEARWRAP;
852 ra = 0; rb = term->rows - 1;
853 ca = 0; cb = term->cols - 1;
854 if (k == 0) {
855 ra = term->row;
856 ca = term->col;
857 } else if (k == 1) {
858 rb = term->row;
859 cb = term->col;
861 for (j = ca; j < term->cols; j++)
862 term->addr[ra][j] = BLANK;
863 for (i = ra+1; i < rb; i++) {
864 for (j = 0; j < term->cols; j++) {
865 term->addr[i][j] = BLANK;
868 for (j = 0; j <= cb; j++)
869 term->addr[rb][j] = BLANK;
870 break;
871 case 'K':
872 CLEARWRAP;
873 ca = 0; cb = term->cols - 1;
874 if (k == 0)
875 ca = term->col;
876 else if (k == 1)
877 cb = term->col;
878 for (j = ca; j <= cb; j++)
879 term->addr[term->row][j] = BLANK;
880 break;
881 case 'L':
882 CLEARWRAP;
883 if (term->row < term->top || term->row > term->bot)
884 break;
885 /* This is implemented naively:
886 1. temporarily change the top margin to current row;
887 2. scroll up as many times as requested;
888 3. restore top margin to previous value. */
889 i = term->top;
890 term->top = term->row;
891 for (j = 0; j < k1; j++)
892 scroll_up(term);
893 term->top = i;
894 break;
895 case 'M':
896 CLEARWRAP;
897 if (term->row < term->top || term->row > term->bot)
898 break;
899 /* This is implemented naively:
900 1. temporarily change the top margin to current row;
901 2. scroll down as many times as requested;
902 3. restore top margin to previous value. */
903 /* TODO:
904 vt102-ug says:
905 "Lines added to bottom of screen have spaces with same character
906 attributes as last line moved up."
907 we need a more flexible scroll_down() to fix this. */
908 i = term->top;
909 term->top = term->row;
910 for (j = 0; j < k1; j++)
911 scroll_down(term);
912 term->top = i;
913 break;
914 case 'P':
915 CLEARWRAP;
916 cell = term->addr[term->row][term->cols-1];
917 cell.code = EMPTY;
918 for (j = term->col; j < term->cols-k1; j++)
919 term->addr[term->row][j] = term->addr[term->row][j+k1];
920 for (j = term->cols-k1; j < term->cols; j++)
921 term->addr[term->row][j] = cell;
922 break;
923 case 'X':
924 CLEARWRAP;
925 for (j = 0; j < k1; j++)
926 term->addr[term->row][term->col+j] = BLANK;
927 break;
928 case 'c':
929 /* Device Attributes (DA) */
930 /* if we were a real terminal, we'd reply with "ESC [ ? 6 c" */
931 /* since there is no application listening, we can ignore this */
932 break;
933 case 'd':
934 CLEARWRAP;
935 term->row = k1 - 1;
936 break;
937 case 'g':
938 /* TODO: clear tab stop */
939 logfmt("NYI: Control Sequence g (TBC)\n");
940 break;
941 case 'h':
942 for (i = 0; i < n; i++)
943 modeswitch(term, private, params[i], 1);
944 break;
945 case 'l':
946 for (i = 0; i < n; i++)
947 modeswitch(term, private, params[i], 0);
948 break;
949 case 'm':
950 sgr(term, n, params);
951 break;
952 case 'n':
953 /* Device Status Report (DSR) */
954 /* if we were a real terminal, we'd send a status reply (e.g. CPR) */
955 /* since there is no application listening, we can ignore this */
956 break;
957 case 'q':
958 /* TODO: set keyboard LEDs */
959 logfmt("NYI: Control Sequence q (DECLL)\n");
960 break;
961 case 'r':
962 if (n == 2) {
963 term->top = MAX(params[0], 1) - 1;
964 term->bot = MAX(params[1], 1) - 1;
965 } else {
966 term->top = 0;
967 term->bot = term->rows - 1;
969 term->row = term->mode & M_ORIGIN ? term->top : 0;
970 term->col = 0;
971 break;
972 case 's':
973 save_cursor(term);
974 break;
975 case 'u':
976 load_cursor(term);
977 break;
978 default:
979 logfmt("UNS: Control Sequence %c\n", byte);
983 #define CHARSET(T) ((T)->cs_array[(T)->cs_index])
984 #define PARCAT(T, B) ((T)->partial[(T)->parlen++] = (B))
985 #define RESET_STATE(T) do { (T)->state = S_ANY; (T)->parlen = 0; } while(0)
986 #define CHARLEN(B) ((B) < 0xE0 ? 2 : ((B) < 0xF0 ? 3 : 4))
988 void
989 parse(Term *term, uint8_t byte)
991 int es;
993 /* Bad suffix for a unicode sequence, dump it and interpret this byte normally. */
994 if (term->state == S_UNI && (byte < 0x80 || byte >= 0xC0)) {
995 addchar(term, 0xFFFD);
996 RESET_STATE(term);
998 if (byte != 0x1B && byte < 0x20 && !(term->mode & M_DISPCTRL)) {
999 ctrlchar(term, byte);
1000 } else {
1001 switch (term->state) {
1002 case S_ANY:
1003 switch (byte) {
1004 case 0x1B:
1005 term->state = S_ESC;
1006 break;
1007 case 0x9B:
1008 term->state = S_CSI;
1009 break;
1010 default:
1011 switch (CHARSET(term)) {
1012 case CS_BMP:
1013 if (byte < 0x80) {
1014 /* single-byte UTF-8, i.e. ASCII */
1015 addchar(term, byte);
1016 } else if (byte >= 0xC0) {
1017 term->unilen = CHARLEN(byte);
1018 PARCAT(term, byte);
1019 term->state = S_UNI;
1020 } else
1021 addchar(term, 0xFFFD);
1022 break;
1023 case CS_ISO:
1024 addchar(term, byte);
1025 break;
1026 case CS_VTG:
1027 addchar(term, cs_vtg[byte]);
1028 break;
1029 case CS_437:
1030 addchar(term, cs_437[byte]);
1031 break;
1034 break;
1035 case S_OSCESC: case S_STRESC:
1036 if (byte == '\\') {
1037 /* do_osc_etc() */
1038 if (term->state == S_OSCESC)
1039 logfmt("NYI: Operating System Sequence\n");
1041 RESET_STATE(term);
1042 term->state = S_ESC;
1043 /*FALLTHROUGH*/
1044 case S_ESC:
1045 es = 1;
1046 if (!term->parlen) {
1047 if (byte == 0x5B) {
1048 term->state = S_CSI;
1049 es = 0;
1050 } else if (byte == 0x5D) {
1051 term->state = S_OSC;
1052 es = 0;
1053 } else if (byte == 'P' || /* DCS */
1054 byte == '_' || /* APC */
1055 byte == '^' || /* PM */
1056 byte == 'X' /* SOS */
1058 term->state = S_STR; /* A string to eat */
1059 es = 0;
1062 if (es) {
1063 if (byte >= 0x20 && byte < 0x30) {
1064 PARCAT(term, byte);
1065 } else {
1066 escseq(term, byte);
1067 RESET_STATE(term);
1070 break;
1071 case S_CSI:
1072 if (byte < 0x40 || byte >= 0x7F) {
1073 PARCAT(term, byte);
1074 } else {
1075 ctrlseq(term, byte);
1076 RESET_STATE(term);
1078 break;
1079 case S_OSC: case S_STR:
1080 /* TODO: set/reset palette entries */
1081 /* Currently this just eats the string */
1082 if (byte == 0x1B) {
1083 if (term->state == S_OSC)
1084 term->state = S_OSCESC;
1085 else
1086 term->state = S_STRESC;
1087 } else if (byte == 13 || byte == 10)
1088 RESET_STATE(term); /* CR or LF assume something broke */
1089 else if (byte == 7) {
1090 /* do_osc_etc() */
1091 if (term->state == S_OSC)
1092 logfmt("NYI: Operating System Sequence\n");
1093 RESET_STATE(term);
1094 } else {
1095 if (term->parlen < MAX_PARTIAL-3)
1096 PARCAT(term, byte);
1097 else
1098 term->state = S_STR;
1100 if (term->partial[0] == 'P' && term->parlen == 8) {
1101 if (do_linux_osc(term))
1102 RESET_STATE(term);
1103 } else if (term->partial[0] == 'R' && term->parlen == 1) {
1104 if (do_linux_osc(term))
1105 RESET_STATE(term);
1108 break;
1109 case S_UNI:
1110 PARCAT(term, byte);
1111 if (term->parlen == term->unilen) {
1112 addchar(term, char_code(term));
1113 RESET_STATE(term);
1115 break;