AmendHub

Download

jcs

/

subtext

/

console.c

 

(View History)

jcs   *: Add malloc annotations Latest amendment: 226 on 2022-07-31

1 /*
2 * Copyright (c) 2021 joshua stein <jcs@jcs.org>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17 #include <stdio.h>
18 #include <string.h>
19
20 #include "subtext.h"
21 #include "ansi.h"
22 #include "console.h"
23 #include "focusable.h"
24 #include "session.h"
25 #include "util.h"
26
27 /* for monaco 9 */
28 #define TEXT_FONT monaco
29 #define TEXT_SIZE 9
30 #define FONT_WIDTH 6
31 #define FONT_HEIGHT 11
32
33 #define CONSOLE_FORCE_REDRAW 1
34 #define CONSOLE_PADDING 6
35 #define CONSOLE_SMOOTH_SCROLLING 0
36
37 void console_idle(struct focusable *focusable, EventRecord *event);
38 void console_close(struct focusable *focusable, EventRecord *event);
39 void console_suspend(struct focusable *focusable, EventRecord *event);
40 void console_resume(struct focusable *focusable, EventRecord *event);
41 void console_update(struct focusable *focusable, EventRecord *event);
42 void console_mouse_down(struct focusable *focusable, EventRecord *event);
43 void console_key_down(struct focusable *focusable, EventRecord *event);
44
45 void console_setup(struct session *session);
46 void console_redraw(struct console *console, short force);
47 short console_bound(struct console *console);
48 void console_erase_chars(struct console *console, short start, short count);
49 void console_shift_chars(struct console *console, short start,
50 short offset);
51 void console_parse_csi(struct console *console);
52
53 struct node_funcs console_node_funcs = {
54 console_setup,
55 console_input,
56 console_output,
57 console_close_from_session
58 };
59
60 struct console *
61 console_init(void)
62 {
63 char title[64];
64 char *sysop_username = NULL;
65 struct console *console;
66 struct focusable *focusable;
67 Rect bounds;
68 short width, height;
69
70 console = xmalloczero(sizeof(struct console), "console_init");
71
72 console->session = session_create("console", "console",
73 &console_node_funcs);
74 if (console->session == NULL) {
75 xfree(&console);
76 warn("No free nodes for a console");
77 return NULL;
78 }
79
80 memset(console->chars, ' ', sizeof(console->chars));
81 console->ncolumns = DEFAULT_TERMINAL_COLUMNS;
82 console->nlines = DEFAULT_TERMINAL_LINES;
83
84 width = CONSOLE_PADDING + (FONT_WIDTH * console->ncolumns) +
85 CONSOLE_PADDING;
86 height = CONSOLE_PADDING + (FONT_HEIGHT * console->nlines) +
87 CONSOLE_PADDING;
88
89 bounds.left = (screenBits.bounds.right - width) / 2;
90 bounds.right = bounds.left + width;
91 bounds.top = GetMBarHeight() +
92 ((screenBits.bounds.bottom - height) / 2);
93 bounds.bottom = bounds.top + height;
94
95 snprintf(title, sizeof(title), "%s: %s: console", PROGRAM_NAME,
96 db->config.hostname);
97 CtoPstr(title);
98
99 console->win = NewWindow(0L, &bounds, title, false, noGrowDocProc,
100 (WindowPtr)-1L, true, 0);
101 if (!console->win)
102 panic("Can't create window");
103
104 focusable = xmalloczero(sizeof(struct focusable), "console focusable");
105 focusable->win = console->win;
106 focusable->cookie = console;
107 focusable->update = console_update;
108 focusable->key_down = console_key_down;
109 focusable->close = console_close;
110 console->focusable = focusable;
111 add_focusable(focusable);
112
113 console->session->cookie = (void *)console;
114 console->session->vt100 = 1;
115 console->session->cp437 = 1;
116 console->session->tspeed = 19200;
117
118 if ((sysop_username = user_first_sysop_username()) != NULL) {
119 strlcpy(console->session->autologin_username, sysop_username,
120 sizeof(console->session->autologin_username));
121 xfree(&sysop_username);
122 }
123
124 return console;
125 }
126
127 void
128 console_idle(struct focusable *focusable, EventRecord *event)
129 {
130 struct console *console = (struct console *)focusable->cookie;
131 struct session *session = console->session;
132 GrafPtr old_port;
133 short n, len, cursor, iac = 0;
134
135 if (session->obuflen == 0)
136 return;
137
138 GetPort(&old_port);
139 SetPort(console->win);
140
141 /* uncursor */
142 cursor = (console->cursor_line * console->ncolumns) +
143 console->cursor_column;
144 if (cursor >= sizeof(console->attrs))
145 panic("console_idle: cursor out of bounds!");
146 console->attrs[cursor] &= ~ATTR_CURSOR;
147 console->attrs[cursor] |= ATTR_DIRTY;
148
149 for (n = 0; n < session->obuflen; n++) {
150 if (console->in_csi) {
151 if (session->obuf[n] == '\33' ||
152 console->csilen >= nitems(console->csi) - 1) {
153 console_parse_csi(console);
154 console->in_csi = 0;
155 console->csi[0] = '\0';
156 console->csilen = 0;
157 } else {
158 console->csi[console->csilen] = session->obuf[n];
159 console->csilen++;
160 console_parse_csi(console);
161 }
162
163 continue;
164 }
165
166 switch (session->obuf[n]) {
167 case '\r':
168 console->cursor_column = 0;
169 break;
170 case '\n':
171 console->cursor_line++;
172 console->cursor_column = 0; /* \r should do this, but JIC */
173 console_bound(console);
174 break;
175 case '\33': /* \e */
176 if (session->obuflen <= n + 1) {
177 /* lone \e at end of buffer, keep it until we see next */
178 session->obuflen = 1;
179 session->obuf[0] = '\33';
180 goto output_done;
181 }
182 if (session->obuf[n + 1] == '[') {
183 console->in_csi = 1;
184 n++;
185 continue;
186 }
187 /* escape but not CSI, fall through */
188 default:
189 console_bound(console);
190 cursor = (console->cursor_line * console->ncolumns) +
191 console->cursor_column;
192 console->chars[cursor] = session->obuf[n];
193 console->attrs[cursor] = console->cur_attr | ATTR_DIRTY;
194 console->cursor_column++;
195 break;
196 }
197 }
198
199 len = session->obuflen;
200 session->obuflen = 0;
201
202 output_done:
203 console_bound(console);
204 console_redraw(console, 0);
205 SetPort(old_port);
206 }
207
208 void
209 console_suspend(struct focusable *focusable, EventRecord *event)
210 {
211 }
212
213 void
214 console_resume(struct focusable *focusable, EventRecord *event)
215 {
216 }
217
218 void
219 console_update(struct focusable *focusable, EventRecord *event)
220 {
221 struct console *console = (struct console *)focusable->cookie;
222 GrafPtr old_win;
223 Rect r;
224 short what = -1;
225
226 if (event != NULL)
227 what = event->what;
228
229 switch (what) {
230 case -1:
231 case updateEvt:
232 console_redraw(console, CONSOLE_FORCE_REDRAW);
233 break;
234 case activateEvt:
235 break;
236 }
237 }
238
239 void
240 console_mouse_down(struct focusable *focusable, EventRecord *event)
241 {
242 }
243
244 void
245 console_key_down(struct focusable *focusable, EventRecord *event)
246 {
247 struct console *console = (struct console *)focusable->cookie;
248 unsigned char k;
249 short km, option_mask, control_mask;
250
251 if (console->session->ibuflen >= nitems(console->session->ibuf))
252 return;
253
254 console->session->last_input_at = Time;
255
256 k = (event->message & charCodeMask);
257 if (event->modifiers & optionKey) {
258 /* these are only correct on a US keyboard */
259 switch (k) {
260 case 0x8d: /* opt+C */
261 /* ^C */
262 console->session->ibuf[console->session->ibuflen++] = CONTROL_C;
263 break;
264 case 0xb6: /* opt+D */
265 /* ^D */
266 console->session->ibuf[console->session->ibuflen++] = CONTROL_D;
267 break;
268 case 0xc2: /* opt+L */
269 console_redraw(console, CONSOLE_FORCE_REDRAW);
270 break;
271 }
272 } else {
273 switch (k) {
274 case 0x1C: /* left */
275 console->session->ibuf[console->session->ibuflen++] = '\33';
276 console->session->ibuf[console->session->ibuflen++] = '[';
277 console->session->ibuf[console->session->ibuflen++] = 'D';
278 break;
279 case 0x1D: /* right */
280 console->session->ibuf[console->session->ibuflen++] = '\33';
281 console->session->ibuf[console->session->ibuflen++] = '[';
282 console->session->ibuf[console->session->ibuflen++] = 'C';
283 break;
284 case 0x1E: /* up */
285 console->session->ibuf[console->session->ibuflen++] = '\33';
286 console->session->ibuf[console->session->ibuflen++] = '[';
287 console->session->ibuf[console->session->ibuflen++] = 'A';
288 break;
289 case 0x1F: /* down */
290 console->session->ibuf[console->session->ibuflen++] = '\33';
291 console->session->ibuf[console->session->ibuflen++] = '[';
292 console->session->ibuf[console->session->ibuflen++] = 'B';
293 break;
294 default:
295 console->session->ibuf[console->session->ibuflen++] = k;
296 if (k == '\r')
297 console->session->ibuf[console->session->ibuflen++] = '\n';
298 }
299 }
300 }
301
302 void
303 console_close(struct focusable *focusable, EventRecord *event)
304 {
305 struct console *console = (struct console *)focusable->cookie;
306 console->session->ending = 1;
307 }
308
309 /* session API */
310
311 void
312 console_setup(struct session *session)
313 {
314 struct console *console = (struct console *)session->cookie;
315 session->terminal_columns = console->ncolumns;
316 session->terminal_lines = console->nlines;
317 }
318
319 short
320 console_output(struct session *session)
321 {
322 struct console *console = (struct console *)session->cookie;
323 console_idle(console->focusable, NULL);
324 return 0;
325 }
326
327 short
328 console_input(struct session *session)
329 {
330 /* nothing to do here, input is fed from main loop */
331 return 0;
332 }
333
334 void
335 console_close_from_session(struct session *session)
336 {
337 struct console *console = (struct console *)session->cookie;
338 session_log(session, "Closing console session");
339 destroy_focusable(console->focusable);
340 xfree(&console);
341 }
342
343 short
344 console_bound(struct console *console)
345 {
346 unsigned short shift_lines, shift_cols, chars_left, pxout,
347 pxout_scroll;
348 RgnHandle rgn;
349 Rect r, r2;
350 GrafPtr old_port;
351 short ret = 0, n;
352
353 while (console->cursor_column > console->ncolumns) {
354 console->cursor_line++;
355 console->cursor_column -= console->ncolumns;
356 }
357
358 if (console->cursor_line >= console->nlines) {
359 shift_lines = console->cursor_line - console->nlines + 1;
360 shift_cols = shift_lines * console->ncolumns;
361 chars_left = (console->nlines * console->ncolumns) - shift_cols;
362 pxout = shift_lines * FONT_HEIGHT;
363
364 GetPort(&old_port);
365 SetPort(console->win);
366 rgn = NewRgn();
367 r.left = console->win->portRect.left + CONSOLE_PADDING;
368 r.top = console->win->portRect.top + CONSOLE_PADDING;
369 r.right = r.left + (console->ncolumns * FONT_WIDTH);
370 r.bottom = r.top + ((console->nlines) * FONT_HEIGHT);
371
372 n = CONSOLE_SMOOTH_SCROLLING ? CONSOLE_SMOOTH_SCROLLING : pxout;
373 pxout_scroll = pxout;
374 while (pxout_scroll > 0) {
375 if (pxout_scroll < n)
376 n = pxout_scroll;
377 ScrollRect(&r, 0, -n, rgn);
378 r2 = r;
379 r2.top = r.bottom - 1;
380 r2.bottom = r.top + n;
381 FillRect(&r2, white);
382 pxout_scroll -= n;
383 }
384 DisposeRgn(rgn);
385
386 BlockMove(console->chars + shift_cols, console->chars, chars_left);
387 BlockMove(console->attrs + shift_cols, console->attrs, chars_left);
388 memset(console->chars + chars_left, ' ', shift_cols);
389 memset(console->attrs + chars_left, console->cur_attr, shift_cols);
390
391 SetPort(old_port);
392
393 console->cursor_line = console->nlines - 1;
394 ret = 1;
395 }
396
397 return ret;
398 }
399
400 #define CONSOLE_ATTRS_DRAWABLE (ATTR_REVERSE | ATTR_BOLD | ATTR_DIRTY)
401
402 void
403 console_redraw(struct console *console, short force)
404 {
405 Rect chunk;
406 GrafPtr old_port;
407 short n, nsize, cell, curbold = -1, line, off, c, firstdirty;
408 unsigned char curattr, a;
409
410 nsize = console->ncolumns * console->nlines;
411
412 GetPort(&old_port);
413 SetPort(console->win);
414
415 TextFont(TEXT_FONT);
416 TextSize(TEXT_SIZE);
417
418 for (line = 0; line < console->nlines; line++) {
419 off = line * console->ncolumns;
420 chunk.top = console->win->portRect.top + CONSOLE_PADDING +
421 (line * FONT_HEIGHT);
422 for (firstdirty = -1, c = 0; c < console->ncolumns; c++) {
423 a = console->attrs[off + c] & CONSOLE_ATTRS_DRAWABLE;
424 if (!force && !(a & ATTR_DIRTY) && firstdirty == -1)
425 continue;
426 console->attrs[off + c] &= ~ATTR_DIRTY;
427
428 if (c == console->ncolumns - 1) {
429 if (firstdirty == -1)
430 firstdirty = c;
431 } else if (firstdirty == -1) {
432 firstdirty = MAX(c - 1, 0);
433 curattr = a;
434 continue;
435 } else if (a == curattr) {
436 continue;
437 }
438
439 /* draw current chunk of dirty chars */
440 chunk.left = console->win->portRect.left + CONSOLE_PADDING +
441 (firstdirty * FONT_WIDTH);
442 chunk.right = chunk.left +
443 ((c - firstdirty) * FONT_WIDTH);
444 chunk.bottom = chunk.top + FONT_HEIGHT;
445 FillRect(&chunk, white);
446
447 if (curattr & ATTR_BOLD) {
448 if (curbold != 1) {
449 TextFace(thePort->txFace | bold | condense);
450 curbold = 1;
451 }
452 } else if (curbold != 0) {
453 TextFace(thePort->txFace & ~(bold | condense));
454 curbold = 0;
455 }
456 MoveTo(chunk.left, chunk.top + FONT_HEIGHT - 2);
457 DrawText(console->chars, off + firstdirty, c - firstdirty);
458
459 if (curattr & ATTR_REVERSE)
460 InvertRect(&chunk);
461
462 if (c != console->ncolumns - 1) {
463 if (a & ATTR_DIRTY)
464 firstdirty = c;
465 else
466 firstdirty = -1;
467 curattr = a;
468 }
469 }
470 }
471
472 /* redraw cursor */
473 chunk.top = console->win->portRect.top + CONSOLE_PADDING +
474 (console->cursor_line * FONT_HEIGHT);
475 chunk.left = console->win->portRect.left + CONSOLE_PADDING +
476 (console->cursor_column * FONT_WIDTH);
477 chunk.bottom = chunk.top + FONT_HEIGHT;
478 chunk.right = chunk.left + FONT_WIDTH;
479 InvertRect(&chunk);
480
481 ValidRect(&console->win->portRect);
482 SetPort(old_port);
483 }
484
485 void
486 console_erase_chars(struct console *console, short start, short count)
487 {
488 Rect eraser;
489 short start_line, end_line, start_col, end_col, line;
490
491 memset(console->chars + start, ' ', count);
492 memset(console->attrs + start, console->cur_attr, count);
493
494 start_line = start / console->ncolumns;
495 start_col = start - (start_line * console->ncolumns);
496 end_line = (start + count) / console->ncolumns;
497 end_col = start + count - (end_line * console->ncolumns);
498
499 for (line = start_line; line <= end_line; line++) {
500 eraser.top = console->win->portRect.top + CONSOLE_PADDING +
501 (line * FONT_HEIGHT);
502 eraser.left = console->win->portRect.left + CONSOLE_PADDING;
503 if (line == start_line)
504 eraser.left += start_col * FONT_WIDTH;
505 eraser.bottom = eraser.top + FONT_HEIGHT;
506 if (line == end_line)
507 eraser.right = eraser.left + (end_col * FONT_WIDTH);
508 else
509 eraser.right = eraser.left + (console->ncolumns * FONT_WIDTH);
510 FillRect(&eraser, console->cur_attr & ATTR_REVERSE ? black : white);
511 }
512 }
513
514 void
515 console_shift_chars(struct console *console, short start, short offset)
516 {
517 Rect mover;
518 RgnHandle rgn;
519 short count;
520
521 /* XXX: this currently only works on whole lines */
522
523 /* move all chars at start by offset, overwriting what's there */
524 count = (console->ncolumns * console->nlines) - start;
525 if (start + offset + count > sizeof(console->chars))
526 /* inserting lines, losing trailing lines */
527 count = sizeof(console->chars) - start - offset;
528 memmove(console->chars + start + offset, console->chars + start,
529 count);
530 memmove(console->attrs + start + offset, console->attrs + start,
531 count);
532
533 if (start % console->ncolumns != 0)
534 warn("TODO partial console_shift_chars");
535 if (offset % console->ncolumns != 0)
536 warn("TODO partial console_shift_chars");
537
538 rgn = NewRgn();
539 mover.top = console->win->portRect.top + CONSOLE_PADDING +
540 ((start / console->ncolumns) * FONT_HEIGHT);
541 if (offset < 0)
542 mover.top -= FONT_HEIGHT;
543 mover.left = console->win->portRect.left + CONSOLE_PADDING;
544 mover.bottom = mover.top + (FONT_HEIGHT * ((count / console->ncolumns) + 1));
545 mover.right = mover.left + (FONT_WIDTH * console->ncolumns);
546 ScrollRect(&mover, 0, (offset / console->ncolumns) * FONT_HEIGHT, rgn);
547 DisposeRgn(rgn);
548 }
549
550 void
551 console_parse_csi(struct console *console)
552 {
553 short x, y;
554 long cursor;
555 short serviced = 0;
556 short param1 = -1, param2 = -1;
557 short parambuflen;
558 short start, count, offset;
559 struct session *session;
560 char parambuf[4];
561 unsigned char c;
562
563 if (console->csilen == 0)
564 return;
565
566 c = console->csi[console->csilen - 1];
567
568 if (c < 'A' || (c > 'Z' && c < 'a') || c > 'z')
569 return;
570
571 switch (c) {
572 case 'A':
573 case 'B':
574 case 'C':
575 case 'D':
576 case 'E':
577 case 'F':
578 case 'G':
579 case 'I':
580 case 'J':
581 case 'K':
582 case 'L':
583 case 'M':
584 case 'S':
585 case 'T':
586 case 'd':
587 case 'g':
588 case 'n':
589 case 's':
590 case 'u':
591 case '7':
592 case '8':
593 /* optional multiplier */
594 if (c == 'J' || c == 'K')
595 param1 = 0;
596 else
597 param1 = 1;
598
599 if (console->csilen > 1) {
600 for (x = 0; x < console->csilen - 1, x < nitems(parambuf); x++) {
601 parambuf[x] = console->csi[x];
602 parambuf[x + 1] = '\0';
603 }
604 param1 = atoi(parambuf);
605 }
606 break;
607 case 'H':
608 case 'f':
609 /* two optional parameters separated by ; each defaulting to 1 */
610 param1 = 1;
611 param2 = 1;
612
613 y = -1;
614 for (x = 0; x < console->csilen; x++) {
615 if (console->csi[x] == ';') {
616 y = x;
617 break;
618 }
619 }
620 if (y == -1)
621 /* CSI 17H -> CSI 17; */
622 y = console->csilen - 1;
623
624 if (y > 0) {
625 for (x = 0; x < y && x < 4; x++) {
626 parambuf[x] = console->csi[x];
627 parambuf[x + 1] = '\0';
628 }
629 param1 = atoi(parambuf);
630
631 if (y < console->csilen - 2) {
632 parambuf[0] = '\0';
633 for (x = 0; x < (console->csilen - 1 - y) && x < 4; x++) {
634 parambuf[x] = console->csi[y + 1 + x];
635 parambuf[x + 1] = '\0';
636 }
637 param2 = atoi(parambuf);
638 }
639 }
640 break;
641 }
642
643 serviced = 1;
644
645 /* uncursor */
646 cursor = (console->cursor_line * console->ncolumns) +
647 console->cursor_column;
648 console->attrs[cursor] &= ~ATTR_CURSOR;
649 console->attrs[cursor] |= ATTR_DIRTY;
650
651 switch (c) {
652 case 'A': /* CUU - cursor up, stop at top of screen */
653 console->cursor_line = MAX(0, console->cursor_line - param1);
654 break;
655 case 'B': /* CUD - cursor down, stop at bottom of screen */
656 console->cursor_line = MIN(console->nlines - 1,
657 console->cursor_line + param1);
658 break;
659 case 'C': /* CUF - cursor forward, stop at screen edge */
660 console->cursor_column = MIN(console->ncolumns - 1,
661 console->cursor_column + param1);
662 break;
663 case 'D': /* CUB - cursor back, stop at left edge of screen */
664 console->cursor_column = MAX(0, console->cursor_column - param1);
665 break;
666 case 'E': /* CNL - cursor next line */
667 console->cursor_column = 0;
668 console->cursor_line = MAX(console->nlines - 1,
669 console->cursor_line + param1);
670 break;
671 case 'F': /* CPL - cursor previous line */
672 console->cursor_column = 0;
673 console->cursor_line = MAX(0, console->cursor_line - param1);
674 break;
675 case 'G': /* CHA - cursor horizontal absolute */
676 console->cursor_column = BOUND(param1, 0, console->ncolumns - 1);
677 break;
678 case 'H': /* CUP - cursor absolute position */
679 case 'f': /* HVP - horizontal vertical position */
680 if (param1 - 1 < 0)
681 console->cursor_line = 0;
682 else if (param1 > console->nlines)
683 console->cursor_line = console->nlines - 1;
684 else
685 console->cursor_line = param1 - 1;
686
687 if (param2 - 1 < 0)
688 console->cursor_column = 0;
689 else if (param2 > console->ncolumns)
690 console->cursor_column = console->ncolumns - 1;
691 else
692 console->cursor_column = param2 - 1;
693 break;
694 case 'J': /* ED - erase in display */
695 switch (param1) {
696 case 0:
697 /* clear from cursor (inclusive) to end of screen */
698 start = (console->cursor_line * console->ncolumns) +
699 console->cursor_column;
700 count = (console->ncolumns * console->nlines) - start;
701 console_erase_chars(console, start, count);
702 break;
703 case 1:
704 /* clear from cursor to beginning of the screen */
705 start = 0;
706 count = (console->cursor_line * console->ncolumns) +
707 console->cursor_column;
708 console_erase_chars(console, start, count);
709 break;
710 case 2:
711 /* clear entire screen, mark everything clean */
712 start = 0;
713 count = console->ncolumns * console->nlines;
714 console_erase_chars(console, start, count);
715 break;
716 }
717 break;
718 case 'K': /* EL - erase in line */
719 switch (param1) {
720 case 0:
721 /* clear from cursor to end of line */
722 start = (console->cursor_line * console->ncolumns) +
723 console->cursor_column;
724 count = console->ncolumns - console->cursor_column;
725 console_erase_chars(console, start, count);
726 break;
727 case 1:
728 /* clear from cursor to beginning of line */
729 start = (console->cursor_line * console->ncolumns);
730 count = console->ncolumns - console->cursor_column;
731 console_erase_chars(console, start, count);
732 break;
733 case 2:
734 /* clear entire line */
735 start = (console->cursor_line * console->ncolumns);
736 count = console->ncolumns;
737 console_erase_chars(console, start, count);
738 break;
739 }
740 break;
741 case 'L': /* IL - insert line */
742 param1 = BOUND(param1, 0, console->nlines - console->cursor_line);
743 if (param1 == 0)
744 break;
745 if (console->cursor_line != console->nlines - 1) {
746 /* shift lines down to insert new ones */
747 start = console->ncolumns * console->cursor_line;
748 offset = console->ncolumns * param1;
749 console_shift_chars(console, start, offset);
750 }
751 /* clear new lines at cursor line */
752 start = console->ncolumns * console->cursor_line;
753 count = console->ncolumns * param1;
754 console_erase_chars(console, start, count);
755 break;
756 case 'M': /* DL - delete line */
757 param1 = BOUND(param1, 0, console->nlines - console->cursor_line);
758 if (param1 == 0)
759 break;
760 if (console->cursor_line != console->nlines - 1) {
761 start = console->ncolumns * (console->cursor_line + param1);
762 offset = console->ncolumns * -param1;
763 console_shift_chars(console, start, offset);
764 }
765 /* fill in new blank lines after lines that moved up */
766 start = console->ncolumns * (console->nlines - param1);
767 count = console->ncolumns * param1;
768 console_erase_chars(console, start, count);
769 break;
770 case 'S': /* SU - scroll up */
771 /* TODO */
772 break;
773 case 'T': /* SD - scroll down */
774 /* TODO */
775 break;
776 case 'd': /* absolute line number */
777 if (param1 < 1)
778 console->cursor_line = 0;
779 else if (param1 > console->nlines)
780 console->cursor_line = console->nlines;
781 else
782 console->cursor_line = param1 - 1;
783 break;
784 case 'g': /* clear tabs, ignore */
785 break;
786 case 'h': /* reset, ignore */
787 break;
788 case 'm': /* graphic changes */
789 parambuf[0] = '\0';
790 parambuflen = 0;
791 param2 = console->cur_attr;
792
793 for (x = 0; x < console->csilen; x++) {
794 /* all the way to console->csilen to catch 'm' */
795 if (console->csi[x] == ';' || console->csi[x] == 'm') {
796 param1 = atoi(parambuf);
797
798 switch (param1) {
799 case 0: /* reset */
800 #ifdef COLOR
801 cur_color = FG_WHITE | BG_BLACK;
802 #endif
803 param2 = 0;
804 break;
805 case 1: /* bold */
806 param2 |= ATTR_BOLD;
807 break;
808 case 4: /* underline */
809 param2 |= ATTR_UNDERLINE;
810 break;
811 case 7: /* reverse */
812 param2 |= ATTR_REVERSE;
813 break;
814 case 22: /* normal color */
815 param2 = 0;
816 break;
817 case 21: /* bold off */
818 param2 &= ~ATTR_BOLD;
819 break;
820 case 24: /* underline off */
821 param2 &= ~ATTR_UNDERLINE;
822 break;
823 case 27: /* inverse off */
824 param2 &= ~ATTR_REVERSE;
825 break;
826 case 30: /* fg black */
827 case 31: /* fg red */
828 case 32: /* fg green */
829 case 33: /* fg yellow */
830 case 34: /* fg blue */
831 case 35: /* fg magenta */
832 case 36: /* fg cyan */
833 case 37: /* fg white */
834 #ifdef COLOR
835 cur_color &= ~0xff;
836 cur_color |= (1 << (param1 - 30));
837 #endif
838 break;
839 case 40: /* bg black */
840 case 41: /* bg red */
841 case 42: /* bg green */
842 case 43: /* bg yellow */
843 case 44: /* bg blue */
844 case 45: /* bg magenta */
845 case 46: /* bg cyan */
846 case 47: /* bg white */
847 #ifdef COLOR
848 cur_color = (1 << (8 + param1 - 40)) |
849 (cur_color & 0xff);
850 #endif
851 break;
852 }
853
854 parambuf[0] = '\0';
855 parambuflen = 0;
856 } else if (parambuflen < 4) {
857 parambuf[parambuflen] = console->csi[x];
858 parambuflen++;
859 parambuf[parambuflen] = '\0';
860 }
861 }
862
863 console->cur_attr = param2;
864 break;
865 case 'n': /* DSR - device status report */
866 switch (param1) {
867 case 5: /* terminal is ready */
868 session = console->session;
869 if (session->ibuflen >= sizeof(session->ibuf) - 4)
870 break;
871 session->ibuf[session->ibuflen++] = ESC;
872 session->ibuf[session->ibuflen++] = '[';
873 session->ibuf[session->ibuflen++] = '0';
874 session->ibuf[session->ibuflen++] = 'n';
875 break;
876 case 6: /* CPR - report cursor position */
877 session = console->session;
878 if (session->ibuflen >= sizeof(session->ibuf) - 10)
879 break;
880 session->ibuf[session->ibuflen++] = ESC;
881 session->ibuf[session->ibuflen++] = '[';
882
883 if (console->cursor_line >= 99)
884 session->ibuf[session->ibuflen++] = '0' +
885 ((console->cursor_line + 1) / 100);
886 if (console->cursor_line >= 9)
887 session->ibuf[session->ibuflen++] = '0' +
888 (((console->cursor_line + 1) % 100) / 10);
889 session->ibuf[session->ibuflen++] = '0' +
890 ((console->cursor_line + 1) % 10);
891
892 session->ibuf[session->ibuflen++] = ';';
893
894 if (console->cursor_column >= 99)
895 session->ibuf[session->ibuflen++] = '0' +
896 ((console->cursor_column + 1) / 100);
897 if (console->cursor_column >= 9)
898 session->ibuf[session->ibuflen++] = '0' +
899 (((console->cursor_column + 1) % 100) / 10);
900 session->ibuf[session->ibuflen++] = '0' +
901 ((console->cursor_column + 1) % 10);
902
903 session->ibuf[session->ibuflen++] = 'R';
904 break;
905 }
906 break;
907 case 's': /* SCO - save cursor position */
908 console->saved_cursor_column = console->cursor_column;
909 console->saved_cursor_line = console->cursor_line;
910 break;
911 case 'u': /* SCO - restore saved cursor position */
912 console->cursor_column = console->saved_cursor_column;
913 console->cursor_line = console->saved_cursor_line;
914 break;
915 default:
916 /*
917 * if the last character is a letter and we haven't serviced
918 * it, assume it's a sequence we don't support and should just
919 * suppress
920 */
921 if (c < 65 || (c > 90 && c < 97) || c > 122)
922 serviced = 0;
923 }
924
925 if (serviced) {
926 console->csilen = 0;
927 console->csi[0] = '\0';
928 console->in_csi = 0;
929 console_bound(console);
930 }
931 }