AmendHub

Download

jcs

/

subtext

/

console.c

 

(View History)

jcs   console: Minor fixes suggested by cppcheck Latest amendment: 372 on 2023-03-08

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