chiark / gitweb /
f481c1a7dfd3810413fc44ed7249039167079f5f
[sympathy.git] / src / vt102.c
1 /*
2  * vt102.c:
3  *
4  * Copyright (c) 2008 James McKenzie <james@fishsoup.dhs.org>,
5  * All rights reserved.
6  *
7  */
8
9 static char rcsid[] = "$Id$";
10
11 /*
12  * $Log$
13  * Revision 1.28  2008/02/14 10:34:30  james
14  * *** empty log message ***
15  *
16  * Revision 1.27  2008/02/14 02:46:45  james
17  * *** empty log message ***
18  *
19  * Revision 1.26  2008/02/14 01:55:57  james
20  * *** empty log message ***
21  *
22  * Revision 1.25  2008/02/13 16:57:29  james
23  * *** empty log message ***
24  *
25  * Revision 1.24  2008/02/13 09:12:21  james
26  * *** empty log message ***
27  *
28  * Revision 1.23  2008/02/07 13:26:35  james
29  * *** empty log message ***
30  *
31  * Revision 1.22  2008/02/07 13:22:51  james
32  * *** empty log message ***
33  *
34  * Revision 1.21  2008/02/07 12:21:16  james
35  * *** empty log message ***
36  *
37  * Revision 1.20  2008/02/07 12:16:04  james
38  * *** empty log message ***
39  *
40  * Revision 1.19  2008/02/07 11:27:02  james
41  * *** empty log message ***
42  *
43  * Revision 1.18  2008/02/07 01:59:25  james
44  * *** empty log message ***
45  *
46  * Revision 1.17  2008/02/07 01:58:28  james
47  * *** empty log message ***
48  *
49  * Revision 1.16  2008/02/07 01:57:46  james
50  * *** empty log message ***
51  *
52  * Revision 1.15  2008/02/07 00:43:27  james
53  * *** empty log message ***
54  *
55  * Revision 1.14  2008/02/07 00:40:23  james
56  * *** empty log message ***
57  *
58  * Revision 1.13  2008/02/07 00:39:59  james
59  * *** empty log message ***
60  *
61  * Revision 1.12  2008/02/07 00:39:13  james
62  * *** empty log message ***
63  *
64  * Revision 1.11  2008/02/06 20:26:58  james
65  * *** empty log message ***
66  *
67  * Revision 1.10  2008/02/06 17:53:28  james
68  * *** empty log message ***
69  *
70  * Revision 1.9  2008/02/06 15:53:22  james
71  * *** empty log message ***
72  *
73  * Revision 1.8  2008/02/06 11:49:47  james
74  * *** empty log message ***
75  *
76  * Revision 1.7  2008/02/06 11:30:37  james
77  * *** empty log message ***
78  *
79  * Revision 1.6  2008/02/05 01:11:46  james
80  * *** empty log message ***
81  *
82  * Revision 1.5  2008/02/04 20:23:55  james
83  * *** empty log message ***
84  *
85  * Revision 1.4  2008/02/04 05:45:55  james
86  * ::
87  *
88  * Revision 1.3  2008/02/04 02:05:06  james
89  * *** empty log message ***
90  *
91  * Revision 1.2  2008/02/04 01:32:39  james
92  * *** empty log message ***
93  *
94  * Revision 1.1  2008/02/03 23:36:41  james
95  * *** empty log message ***
96  *
97  */
98
99
100 /* Termcap he say:
101
102 vt102|dec vt102:\
103         :mi:\
104         :al=\E[L:dc=\E[P:dl=\E[M:ei=\E[4l:im=\E[4h:tc=vt100:
105
106 vt100|vt100-am|dec vt100 (w/advanced video):\
107         :am:bs:ms:xn:xo:\
108         :co#80:it#8:li#24:vt#3:\
109         :DO=\E[%dB:LE=\E[%dD:RA=\E[?7l:RI=\E[%dC:SA=\E[?7h:\
110         :UP=\E[%dA:\
111         :ac=``aaffggjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~:\
112         :ae=^O:as=^N:bl=^G:cb=\E[1K:cd=\E[J:ce=\E[K:cl=\E[H\E[J:\
113         :cm=\E[%i%d;%dH:cr=^M:cs=\E[%i%d;%dr:ct=\E[3g:do=^J:\
114         :eA=\E(B\E)0:ho=\E[H:kb=^H:kd=\EOB:ke=\E[?1l\E>:kl=\EOD:\
115         :kr=\EOC:ks=\E[?1h\E=:ku=\EOA:le=^H:mb=\E[5m:md=\E[1m:\
116         :me=\E[m\017:mr=\E[7m:nd=\E[C:rc=\E8:\
117         :rs=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h:sc=\E7:se=\E[m:\
118         :sf=^J:so=\E[7m:sr=\EM:st=\EH:ta=^I:ue=\E[m:up=\E[A:\
119         :us=\E[4m:tc=vt100+fnkeys:
120
121 vt100+fnkeys|dec vt100 numeric keypad:\
122         :k0=\EOy:k5=\EOt:k6=\EOu:k7=\EOv:k8=\EOl:k9=\EOw:k;=\EOx:\
123         :tc=vt100+pfkeys:
124
125 vt100+pfkeys|dec vt100 numeric keypad:\
126         :@8=\EOM:k1=\EOP:k2=\EOQ:k3=\EOR:k4=\EOS:tc=vt100+keypad:
127
128 vt100+keypad|dec vt100 numeric keypad no fkeys:\
129         :K1=\EOq:K2=\EOr:K3=\EOs:K4=\EOp:K5=\EOn:
130
131 */
132
133 /*
134 so the parser needs to be able to at least do
135 CTRL-G
136 CTRL-H
137 CTRL-I
138 CTRL-J
139 CTRL-M
140 CTRL-N
141
142 CTRL-O
143 ESC7
144 ESC8
145 ESCH
146 ESCM
147 ESC> 
148
149 ESC[%dA
150 ESC[%dB
151 ESC[%dC
152 ESC[%dD
153 ESC[H
154 ESC[%d;%dH
155 ESC[J
156 ESC[K
157 ESC[1K
158 ESC[L
159 ESC[M
160 ESC[P
161
162 ESC[3g
163 ESC[4h
164 ESC[4l
165 ESC[m 
166 ESC[1m
167 ESC[4m
168 ESC[5m
169 ESC[7m
170 ESC[%d;%dr
171
172
173 ESC[?3l 
174 ESC[?4l 
175 ESC[?5l
176 ESC[?7h
177 ESC[?7h
178 ESC[?7l
179 ESC[?8h
180
181 ESC(B
182 ESC)0
183
184
185 TODO:
186
187 ESC(B
188 ESC)0
189
190 CTRL-O
191
192
193 */
194
195 #include "project.h"
196
197
198
199 static inline int
200 csi_ender (int c)
201 {
202   if ((c >= 'a') && (c <= 'z'))
203     return 1;
204   if ((c >= 'A') && (c <= 'Z'))
205     return 1;
206   return 0;
207 }
208
209 static inline int
210 scs_starter (int c)
211 {
212   switch (c)
213     {
214     case '(':
215     case ')':
216       return 1;
217     }
218   return 0;
219 }
220
221 static inline int
222 csi_starter (int c)
223 {
224   switch (c)
225     {
226     case '[':
227       return 1;
228     }
229   return 0;
230 }
231
232 static inline int
233 in_margins (VT102 * v, CRT_Pos p)
234 {
235   if (v->pos.x < v->top_margin.x)
236     return 0;
237   if (v->pos.y < v->top_margin.y)
238     return 0;
239
240   if (v->pos.x > v->bottom_margin.x)
241     return 0;
242   if (v->pos.y > v->bottom_margin.y)
243     return 0;
244
245   return 1;
246 }
247
248 void
249 vt102_log_line (Context * c, int line)
250 {
251   CRT_Pos e = { VT102_COLS - 1, line };
252   CRT_Pos p = { 0, line };
253   char logbuf[VT102_COLS + 1];
254
255   if (!c->l)
256     return;
257
258
259   for (; e.x > 0; --e.x)
260     {
261       if (c->v->crt.screen[CRT_ADDR_POS (&e)].chr != ' ')
262         break;
263     }
264
265   for (; p.x <= e.x; ++p.x)
266     {
267       int ch = c->v->crt.screen[CRT_ADDR_POS (&p)].chr;
268       if (ch < 32)
269         ch = ' ';
270       if (ch > 126)
271         ch = ' ';
272       logbuf[p.x] = ch;
273     }
274   logbuf[p.x] = 0;
275
276   c->l->log (c->l, logbuf);
277 }
278
279 /*Called for every upward scroll with same args*/
280 void
281 vt102_history (Context * c, CRT_Pos t, CRT_Pos b)
282 {
283 /*Only log if it scrolls off the top*/
284   if (t.y)
285     return;
286
287   t.x = 0;
288   history_add (c->h, &(c->v->crt.screen[CRT_ADDR_POS (&t)]));
289 }
290
291 void
292 vt102_clip_cursor (VT102 * v, CRT_Pos tl, CRT_Pos br)
293 {
294   if (v->pos.x < tl.x)
295     v->pos.x = tl.x;
296   if (v->pos.y < tl.y)
297     v->pos.y = tl.y;
298
299   if (v->pos.x > br.x)
300     v->pos.x = br.x;
301   if (v->pos.y > br.y)
302     v->pos.y = br.y;
303 }
304
305
306 void
307 vt102_cursor_normalize (VT102 * v)
308 {
309   CRT_Pos *top, *bottom;
310
311   if (v->private_modes[VT102_PRIVATE_MODE_ORIGIN_MODE])
312     {
313       vt102_clip_cursor (v, v->top_margin, v->bottom_margin);
314     }
315   else
316     {
317       vt102_clip_cursor (v, v->screen_start, v->screen_end);
318     }
319 }
320
321
322 void
323 vt102_cursor_carriage_return (VT102 * v)
324 {
325    /*FISH*/ v->pos.x = v->top_margin.x;
326   v->pending_wrap = 0;
327 }
328
329 void
330 vt102_cursor_advance_line (Context * c)
331 {
332   VT102 *v = c->v;
333   int couldscroll = in_margins (v, v->pos);
334
335 /*have wraped off end of last line in scrolling region */
336 /* (|| not necessary, but shuts compiler up */
337   if (((v->pos.y == v->bottom_margin.y) || (v->pos.y == v->screen_end.y)) &&
338       (couldscroll))
339     {
340       vt102_log_line (c, v->pos.y);
341
342       vt102_history (c, v->top_margin, v->bottom_margin);
343
344       crt_scroll_up (&v->crt, v->top_margin, v->bottom_margin, 1);
345       return;
346     }
347
348   v->pos.y++;
349   v->pending_wrap = 0;
350 }
351
352
353 void
354 vt102_cursor_advance (Context * c)
355 {
356   VT102 *v = c->v;
357
358   if (v->pos.x < v->bottom_margin.x)
359     {
360 /*Normal advance*/
361       v->pos.x++;
362       v->pending_wrap = 0;
363       return;
364     }
365   v->pending_wrap++;
366 }
367
368
369 void
370 vt102_do_pending_wrap (Context * c)
371 {
372   VT102 *v = c->v;
373   int couldscroll = in_margins (v, v->pos);
374   int autowrap = v->private_modes[VT102_PRIVATE_MODE_AUTO_WRAP];
375
376   if (!v->pending_wrap)
377     return;
378
379 #if 0
380   fprintf (stderr, "ca: (%d,%d) autowrap %d couldscroll %d\n", v->pos.x,
381            v->pos.y, autowrap, couldscroll);
382 #endif
383
384 /*End of line but no autowrap, nothing to do*/
385   if (!autowrap)
386     return;
387
388 /*End of screen and not allowed to scroll, nothing to do*/
389   if ((v->pos.y == v->screen_end.y) && (!couldscroll))
390     return;
391
392   if (couldscroll)
393     {
394       v->pos.x = v->top_margin.x;
395     }
396   else
397     {
398       v->pos.x = 0;
399     }
400
401   vt102_cursor_advance_line (c);
402 }
403
404
405 void
406 vt102_cursor_retard (VT102 * v)
407 {
408   if (v->pos.x != v->top_margin.x)
409     v->pos.x--;
410
411   v->pending_wrap = 0;
412 }
413
414 void
415 vt102_reset_tabs (VT102 * v)
416 {
417   int i;
418
419   memset (v->tabs, 0, sizeof (v->tabs));
420
421   for (i = 0; i < VT102_COLS; i += 8)
422     {
423       v->tabs[i]++;
424     }
425 }
426 void
427 vt102_cursor_advance_tab (VT102 * v)
428 {
429   if (v->pos.x == v->bottom_margin.x)
430     return;
431   while (v->pos.x < v->bottom_margin.x)
432     {
433       v->pos.x++;
434       if (v->tabs[v->pos.x])
435         break;
436     }
437   v->pending_wrap = 0;
438 }
439
440 vt102_cursor_home (VT102 * v)
441 {
442   v->pos = v->top_margin;
443   vt102_cursor_normalize (v);
444   v->pending_wrap = 0;
445
446 }
447
448 vt102_cursor_absolute (VT102 * v, int x, int y)
449 {
450   if (v->private_modes[VT102_PRIVATE_MODE_ORIGIN_MODE])
451     {
452       v->pos.x = x + v->top_margin.x;
453       v->pos.y = y + v->top_margin.x;
454     }
455   else
456     {
457       v->pos.x = x;
458       v->pos.y = y;
459     }
460   vt102_cursor_normalize (v);
461   v->pending_wrap = 0;
462 }
463
464 vt102_cursor_relative (VT102 * v, int x, int y)
465 {
466   v->pos.x += x;
467   v->pos.y += y;
468   vt102_cursor_normalize (v);
469   v->pending_wrap = 0;
470 }
471
472
473
474 void
475 vt102_delete_from_line (VT102 * v, CRT_Pos p)
476 {
477   int n = v->bottom_margin.x - p.x;
478
479   if (n < 0)
480     return;
481
482   if (n)
483     {
484
485       memmove (&v->crt.screen[CRT_ADDR_POS (&p)],
486                &v->crt.screen[CRT_ADDR_POS (&p) + 1], sizeof (CRT_CA) * n);
487     }
488
489   v->crt.screen[CRT_ADDR (p.y, v->bottom_margin.x)].chr = ' ';
490 /*But not attr due to vt102 bug*/
491 }
492
493 void
494 vt102_insert_into_line (VT102 * v, CRT_Pos p)
495 {
496   int n = v->bottom_margin.x - p.x;
497
498   if (n < 0)
499     return;
500
501   if (n)
502     {
503
504       memmove (&v->crt.screen[CRT_ADDR_POS (&p) + 1],
505                &v->crt.screen[CRT_ADDR_POS (&p)], sizeof (CRT_CA) * n);
506     }
507
508   v->crt.screen[CRT_ADDR (p.y, v->bottom_margin.x)].chr = ' ';
509   v->crt.screen[CRT_ADDR (p.y, v->bottom_margin.x)].attr = CRT_ATTR_NORMAL;
510   v->crt.screen[CRT_ADDR (p.y, v->bottom_margin.x)].color = CRT_COLOR_NORMAL;
511 }
512
513
514
515 void
516 vt102_change_mode (VT102 * v, int private, char *ns, int set)
517 {
518   int m;
519
520
521   if (*ns)
522     {
523       m = atoi (ns);
524     }
525   else
526     {
527       m = 1;
528     }
529
530   if (m < 0)
531     return;
532   if (m >= VT102_NMODES)
533     return;
534
535   if (private)
536     {
537       v->private_modes[m] = set;
538       switch (m)
539         {
540         case VT102_PRIVATE_MODE_CURSOR_MODE:
541           if (v->application_keypad_mode)
542             v->private_modes[m] = 0;
543 #if 0
544           fprintf (stderr, "APPLICATION CURSOR MODE %d wanted %d\n",
545                    v->private_modes[m], set);
546 #endif
547           break;
548         case VT102_PRIVATE_MODE_ORIGIN_MODE:
549           vt102_cursor_home (v);
550           break;
551         }
552
553     }
554   else
555     v->modes[m] = set;
556
557 #if 0
558   fprintf (stderr, "mode set=%d private=%d num=%d\n", set, private, m);
559 #endif
560 }
561
562 void
563 vt102_parse_mode_string (VT102 * v, char *buf, int len)
564 {
565   int private = 0;
566   char last = buf[len - 1];
567   char num[4];
568   int o;
569
570   memset (num, 0, sizeof (num));
571   o = sizeof (num) - 1;
572
573   len--;
574
575   if (*buf == '?')
576     {
577       private++;
578       buf++;
579       len--;
580     }
581
582   if (len < 0)
583     return;
584
585   while (len--)
586     {
587       if (*buf == ';')
588         {
589           vt102_change_mode (v, private, &num[o], last == 'h');
590           memset (num, 0, sizeof (num));
591           o = sizeof (num) - 1;
592           buf++;
593           continue;
594         }
595
596       num[0] = num[1];
597       num[1] = num[2];
598       num[2] = *buf;
599
600       if (o)
601         o--;
602
603       buf++;
604     }
605
606   vt102_change_mode (v, private, &num[o], last == 'h');
607
608 }
609
610
611 void
612 vt102_change_attr (VT102 * v, char *na)
613 {
614   int a;
615
616
617   if (*na)
618     {
619       a = atoi (na);
620     }
621   else
622     {
623       a = 0;
624     }
625
626   switch (a)
627     {
628     case 0:
629       v->attr = CRT_ATTR_NORMAL;
630       v->color = CRT_COLOR_NORMAL;
631       break;
632     case 1:
633       v->attr |= CRT_ATTR_BOLD;
634       break;
635     case 21:
636     case 22:
637       v->attr &= ~CRT_ATTR_BOLD;
638       break;
639     case 4:
640       v->attr |= CRT_ATTR_UNDERLINE;
641       break;
642     case 24:
643       v->attr &= ~CRT_ATTR_UNDERLINE;
644       break;
645     case 5:
646       v->attr |= CRT_ATTR_BLINK;
647       break;
648     case 25:
649       v->attr &= ~CRT_ATTR_BLINK;
650       break;
651     case 7:
652       v->attr |= CRT_ATTR_REVERSE;
653       break;
654     case 27:
655       v->attr &= ~CRT_ATTR_REVERSE;
656       break;
657     case 30:
658     case 31:
659     case 32:
660     case 33:
661     case 34:
662     case 35:
663     case 36:
664     case 37:
665       v->color &= ~CRT_COLOR_FG_MASK;
666       v->color |= ((a - 30) << CRT_COLOR_FG_SHIFT) & CRT_COLOR_FG_MASK;
667       break;
668     case 90:
669     case 91:
670     case 92:
671     case 93:
672     case 94:
673     case 95:
674     case 96:
675     case 97:
676       v->color &= ~CRT_COLOR_FG_MASK;
677       v->color |=
678         (((a -
679            90) | CRT_COLOR_INTENSITY) << CRT_COLOR_FG_SHIFT) &
680         CRT_COLOR_FG_MASK;
681       break;
682     case 39:
683     case 99:
684       v->color &= ~CRT_COLOR_FG_MASK;
685       v->color |=
686         (CRT_FGCOLOR_NORMAL << CRT_COLOR_FG_SHIFT) & CRT_COLOR_FG_MASK;
687       break;
688     case 40:
689     case 41:
690     case 42:
691     case 43:
692     case 44:
693     case 45:
694     case 46:
695     case 47:
696       v->color &= ~CRT_COLOR_BG_MASK;
697       v->color |= ((a - 40) << CRT_COLOR_BG_SHIFT) & CRT_COLOR_BG_MASK;
698       break;
699     case 100:
700     case 101:
701     case 102:
702     case 103:
703     case 104:
704     case 105:
705     case 106:
706     case 107:
707       v->color &= ~CRT_COLOR_BG_MASK;
708       v->color |=
709         (((a -
710            100) | CRT_COLOR_INTENSITY) << CRT_COLOR_BG_SHIFT) &
711         CRT_COLOR_BG_MASK;
712       break;
713     case 49:
714     case 109:
715       v->color &= ~CRT_COLOR_BG_MASK;
716       v->color |=
717         (CRT_BGCOLOR_NORMAL << CRT_COLOR_BG_SHIFT) & CRT_COLOR_BG_MASK;
718       break;
719
720     default:
721       ;
722 #if 0
723       fprintf (stderr, "unhandled SGR %d\n", a);
724 #endif
725     }
726
727 }
728
729
730 void
731 vt102_parse_attr_string (VT102 * v, char *buf, int len)
732 {
733   int private = 0;
734   char last = buf[len - 1];
735   char num[4];
736   int o;
737
738   memset (num, 0, sizeof (num));
739   o = sizeof (num) - 1;
740
741   len--;
742
743   if (len < 0)
744     return;
745
746   while (len--)
747     {
748       if (*buf == ';')
749         {
750           vt102_change_attr (v, &num[o]);
751           memset (num, 0, sizeof (num));
752           o = sizeof (num) - 1;
753           buf++;
754           continue;
755         }
756
757       num[0] = num[1];
758       num[1] = num[2];
759       num[2] = *buf;
760
761       if (o)
762         o--;
763
764       buf++;
765     }
766   vt102_change_attr (v, &num[o]);
767 }
768
769 void
770 vt102_save_state (VT102 * v)
771 {
772   v->saved.pos = v->pos;
773   v->saved.attr = v->attr;
774   v->saved.color = v->color;
775   v->saved.origin_mode = v->private_modes[VT102_PRIVATE_MODE_ORIGIN_MODE];
776 }
777
778 void
779 vt102_restore_state (VT102 * v)
780 {
781   v->pos = v->saved.pos;
782   v->attr = v->saved.attr;
783   v->color = v->saved.color;
784   v->private_modes[VT102_PRIVATE_MODE_ORIGIN_MODE] = v->saved.origin_mode;
785   vt102_cursor_normalize (v);
786   v->pending_wrap = 0;
787 }
788
789 void
790 vt102_scs (Context * c, int g, int s)
791 {
792 /*Ignoring charsets*/
793 }
794
795 void
796 vt102_parse_esc (Context * c, int ch)
797 {
798   VT102 *v = c->v;
799   switch (ch)
800     {
801     case 'E':
802       if (v->pos.y == v->bottom_margin.y)
803         {
804           vt102_log_line (c, v->pos.y);
805           vt102_history (c, v->top_margin, v->bottom_margin);
806           crt_scroll_up (&v->crt, v->top_margin, v->bottom_margin, 1);
807         }
808       else
809         {
810           vt102_cursor_relative (v, 0, 1);
811         }
812       break;
813     case 'H':
814       v->tabs[v->pos.x]++;
815       break;
816     case 'M':
817       if (v->pos.y == v->top_margin.y)
818         {
819           crt_scroll_down (&v->crt, v->top_margin, v->bottom_margin, 1);
820         }
821       else
822         {
823           vt102_cursor_relative (v, 0, -1);
824         }
825       break;
826     case '7':
827       vt102_save_state (v);
828       break;
829     case '8':
830       vt102_restore_state (v);
831       break;
832     case '=':
833       v->application_keypad_mode = 1;
834       break;
835     case '>':
836       v->application_keypad_mode = 0;
837       break;
838     default:
839 #if 0
840       fprintf (stderr, "unhandled ESC \\033 \\%03o (ESC %c)\n", ch,
841                (ch < 32) ? '?' : ch);
842 #endif
843       ;
844     }
845 }
846 void
847 vt102_parse_csi (Context * c, char *buf, int len)
848 {
849   char last;
850   char *ptr;
851   char *arg = buf + 1;
852   int narg;
853
854   VT102 *v = c->v;
855
856   buf[len] = 0;
857
858   last = buf[len - 1];
859 #if 0
860   buf[len - 1] = 0;
861 #endif
862
863   if (len > 2)
864     {
865       narg = atoi (arg);
866     }
867   else
868     {
869       narg = 1;
870     }
871
872   switch (buf[0])
873     {
874     case '[':
875       switch (last)
876         {
877         case 'A':
878           vt102_cursor_relative (v, 0, -narg);
879           break;
880         case 'B':
881           vt102_cursor_relative (v, 0, narg);
882           break;
883         case 'C':
884           vt102_cursor_relative (v, narg, 0);
885           break;
886         case 'D':
887           vt102_cursor_relative (v, -narg, 0);
888           break;
889         case 'E':
890           vt102_cursor_relative (v, 0, narg);
891           vt102_cursor_carriage_return (v);
892           break;
893         case 'F':
894           vt102_cursor_relative (v, 0, -narg);
895           vt102_cursor_carriage_return (v);
896           break;
897         case 'G':
898           vt102_cursor_absolute (v, narg - 1, v->pos.y);
899           break;
900         case 'H':
901         case 'f':
902           {
903             int x, y;
904
905             y = narg - 1;
906
907             ptr = index (arg, ';');
908             if (ptr)
909               x = atoi (ptr + 1) - 1;
910             else
911               x = 0;
912
913             vt102_cursor_absolute (v, x, y);
914           }
915           break;
916         case 'J':
917           switch (narg)
918             {
919             case 1:
920               crt_erase (&v->crt, v->pos, v->screen_end, 1);
921               break;
922             case 2:
923               crt_erase (&v->crt, v->screen_start, v->screen_end, 1);
924               break;
925             }
926           break;
927         case 'K':
928           {
929             CRT_Pos ls = { 0, v->pos.y };
930             CRT_Pos le = { VT102_COLS - 1, v->pos.y };
931             if (len == 2)
932               narg = 0;         /*Different default */
933
934             switch (narg)
935               {
936               case 0:
937                 crt_erase (&v->crt, v->pos, le, 1);
938                 break;
939               case 1:
940                 crt_erase (&v->crt, ls, v->pos, 1);
941                 break;
942               case 2:
943                 crt_erase (&v->crt, ls, le, 1);
944                 break;
945               }
946           }
947           break;
948
949         case 'P':
950           while (narg--)
951             vt102_delete_from_line (v, v->pos);
952           break;
953         case 'L':
954           if ((v->pos.y >= v->top_margin.y)
955               && (v->pos.y <= v->bottom_margin.y))
956             {
957               while (narg--)
958                 crt_scroll_down (&v->crt, v->pos, v->bottom_margin, 1);
959             }
960           break;
961
962         case 'M':
963           if ((v->pos.y >= v->top_margin.y)
964               && (v->pos.y <= v->bottom_margin.y))
965             {
966               while (narg--)
967                 {
968                   vt102_history (c, v->pos, v->bottom_margin);
969                   crt_scroll_up (&v->crt, v->pos, v->bottom_margin, 1);
970                 }
971             }
972           break;
973
974         case 'g':
975           if (len == 2)
976             narg = 0;           /*Different default */
977
978           switch (narg)
979             {
980             case 0:
981               v->tabs[v->pos.x] = 0;
982               break;
983             case 3:
984               memset (v->tabs, 0, sizeof (v->tabs));
985               break;
986             }
987           break;
988
989         case 'h':
990         case 'l':
991           vt102_parse_mode_string (v, &buf[1], len - 1);
992           break;
993
994         case 'm':
995           vt102_parse_attr_string (v, &buf[1], len - 1);
996           break;
997         case 'r':
998           v->top_margin = v->screen_start;
999           v->bottom_margin = v->screen_end;
1000
1001           if ((len > 2) && (ptr = index (arg, ';')))
1002             {
1003               ptr++;
1004               v->top_margin.y = narg - 1;
1005               v->bottom_margin.y = atoi (ptr) - 1;
1006             }
1007
1008           if (v->top_margin.y < v->screen_start.y)
1009             v->top_margin.y = v->screen_start.y;
1010           if (v->top_margin.y > v->screen_end.y)
1011             v->top_margin.y = v->screen_end.y;
1012           if (v->bottom_margin.y < v->screen_start.y)
1013             v->bottom_margin.y = v->screen_start.y;
1014           if (v->bottom_margin.y > v->screen_end.y)
1015             v->bottom_margin.y = v->screen_end.y;
1016
1017           vt102_cursor_home (v);
1018           break;
1019         case 's':
1020           v->saved.pos = v->pos;
1021           break;
1022         case 'u':
1023           v->pos = v->saved.pos;
1024           vt102_cursor_normalize (v);
1025           v->pending_wrap = 0;
1026           break;
1027
1028         default:
1029 #if 0
1030           fprintf (stderr, "unhandled CSI  \\033%s\n", buf, buf[0]);
1031 #endif
1032           ;
1033         }
1034       break;
1035     default:
1036 #if 0
1037       fprintf (stderr, "unhandled CSI  \\033%s\n", buf, buf[0]);
1038 #endif
1039       ;
1040     }
1041
1042
1043
1044 }
1045
1046 void
1047 vt102_status_line (VT102 * v, char *str)
1048 {
1049   int i = VT102_COLS;
1050   CRT_CA *ca = &v->crt.screen[CRT_ADDR (VT102_STATUS_ROW, 0)];
1051
1052   while (i--)
1053     {
1054       ca->attr = CRT_ATTR_REVERSE;
1055       ca->color = CRT_COLOR_NORMAL;
1056       ca->chr = *str;
1057       if (*str)
1058         str++;
1059       ca++;
1060     }
1061
1062 }
1063
1064
1065 void
1066 vt102_parse_char (Context * c, int ch)
1067 {
1068   VT102 *v = c->v;
1069   VT102_parser *p = &v->parser;
1070
1071 #if 0
1072   fprintf (stderr, "char %c pc %d %d %d   %d %d\n", (c < 32) ? 32 : c, c,
1073            p->in_csi, p->in_escape, v->pos.x, v->pos.y);
1074 #endif
1075   if (p->in_csi)
1076     {
1077       p->csi_buf[p->csi_ptr++] = ch;
1078       if (csi_ender (ch) || (p->csi_ptr == VT102_CSI_LEN))
1079         {
1080           vt102_parse_csi (c, p->csi_buf, p->csi_ptr);
1081           p->in_csi = 0;
1082         }
1083     }
1084   else if (p->in_escape)
1085     {
1086       if (csi_starter (ch))
1087         {
1088           p->csi_ptr = 0;
1089           p->csi_buf[p->csi_ptr++] = ch;
1090           p->in_csi++;
1091           p->in_escape = 0;
1092         }
1093       else if (scs_starter (ch))
1094         {
1095           p->in_scs = ch;
1096           p->in_escape = 0;
1097         }
1098       else
1099         {
1100           p->in_escape = 0;
1101           vt102_parse_esc (c, ch);
1102         }
1103     }
1104   else if (p->in_scs)
1105     {
1106       vt102_scs (c, p->in_scs, ch);
1107       p->in_scs = 0;
1108     }
1109   else
1110     {
1111
1112       switch (ch)
1113         {
1114          /*NUL*/ case 0:
1115          /*SOH*/ case 1:
1116          /*STX*/ case 2:
1117          /*ETX*/ case 3:
1118          /*EOT*/ case 4:
1119           break;
1120          /*ENQ*/ case 5:
1121           c->t->xmit (c->t, "vt102", 5);
1122           break;
1123          /*ACK*/ case 6:
1124          /*BEL*/ case 7:
1125           break;
1126          /*BS*/ case 8:
1127           vt102_cursor_retard (c->v);
1128           break;
1129          /*HT*/ case 9:
1130           vt102_cursor_advance_tab (c->v);
1131           break;
1132          /*LF*/ case 10:
1133          /*VT*/ case 11:
1134          /*FF*/ case 12:
1135           vt102_cursor_advance_line (c);
1136           if (!v->modes[VT102_MODE_NEWLINE_MODE])
1137             break;
1138          /*CR*/ case 13:
1139           vt102_cursor_carriage_return (v);
1140           break;
1141          /*SO*/ case 14:
1142           /*select G1 */
1143           /*Ignoring charsets */
1144           break;
1145          /*SI*/ case 15:
1146           /*select G0 */
1147           /*Ignoring charsets */
1148           break;
1149          /*DLE*/ case 16:
1150         /*DC1 */ case 17:
1151         /*DC2 */ case 18:
1152         /*DC3 */ case 19:
1153         /*DC4 */ case 20:
1154          /*NAK*/ case 21:
1155          /*SYN*/ case 22:
1156          /*ETB*/ case 23:
1157          /*CAN*/ case 24:
1158          /*EM*/ case 25:
1159          /*SUB*/ case 26:
1160           break;
1161          /*ESC*/ case 27:
1162           p->in_escape++;
1163           return;
1164          /*FS*/ case 28:
1165          /*GS*/ case 29:
1166          /*RS*/ case 30:
1167          /*US*/ case 31:
1168          /*DEL*/ case 127:
1169           break;
1170         /*regular character */ default:
1171           vt102_do_pending_wrap (c);
1172
1173           if (v->modes[VT102_MODE_INSERT])
1174             vt102_insert_into_line (v, v->pos);
1175
1176           v->crt.screen[CRT_ADDR_POS (&v->pos)].chr = ch;
1177           v->crt.screen[CRT_ADDR_POS (&v->pos)].attr = v->attr;
1178           v->crt.screen[CRT_ADDR_POS (&v->pos)].color = v->color;
1179           vt102_cursor_advance (c);
1180         }
1181     }
1182
1183   v->crt.pos = v->pos;
1184   v->crt.hide_cursor =
1185     v->private_modes[VT102_PRIVATE_MODE_SHOW_CURSOR] ? 0 : 1;
1186
1187   if (v->current_line.y != v->pos.y)
1188     {
1189       vt102_log_line (c, v->current_line.y);
1190       v->current_line = v->pos;
1191     }
1192
1193 }
1194
1195 vt102_parse (Context * c, char *buf, int len)
1196 {
1197   while (len--)
1198     vt102_parse_char (c, *(buf++));
1199 }
1200
1201
1202 void
1203 vt102_parser_reset (VT102_parser * p)
1204 {
1205   p->in_csi = 0;
1206   p->in_escape = 0;
1207   p->csi_ptr = 0;
1208   p->in_scs = 0;
1209 }
1210
1211
1212 void
1213 vt102_send (Context * c, uint8_t key)
1214 {
1215   uint8_t ch;
1216
1217   if (!c->t)
1218     return;
1219
1220 #if 0
1221   fprintf (stderr, "vts: %d(%c)\n", key, (key > 31) ? key : ' ');
1222 #endif
1223   if ((key > 31) && (key < 127))
1224     {
1225       c->t->xmit (c->t, &key, 1);
1226       return;
1227     }
1228
1229   switch (key)
1230     {
1231      /*NUL*/ case 0:
1232      /*SOH*/ case 1:
1233      /*STX*/ case 2:
1234      /*ETX*/ case 3:
1235      /*EOT*/ case 4:
1236      /*ENQ*/ case 5:
1237      /*ACK*/ case 6:
1238      /*BEL*/ case 7:
1239      /*BS*/ case 8:
1240      /*HT*/ case 9:
1241      /*LF*/ case 10:
1242      /*VT*/ case 11:
1243      /*FF*/ case 12:
1244       c->t->xmit (c->t, &key, 1);
1245       break;
1246      /*CR*/ case 13:
1247       c->t->xmit (c->t, &key, 1);
1248       if (c->v->modes[VT102_MODE_NEWLINE_MODE])
1249         {
1250           ch = 10;
1251           c->t->xmit (c->t, &ch, 1);
1252         }
1253       break;
1254      /*SO*/ case 14:
1255      /*SI*/ case 15:
1256      /*DLE*/ case 16:
1257     /*DC1 */ case 17:
1258     /*DC2 */ case 18:
1259     /*DC3 */ case 19:
1260     /*DC4 */ case 20:
1261      /*NAK*/ case 21:
1262      /*SYN*/ case 22:
1263      /*ETB*/ case 23:
1264      /*CAN*/ case 24:
1265      /*EM*/ case 25:
1266      /*SUB*/ case 26:
1267       c->t->xmit (c->t, &key, 1);
1268       break;
1269      /*ESC*/ case 27:
1270      /*FS*/ case 28:
1271      /*GS*/ case 29:
1272      /*RS*/ case 30:
1273      /*US*/ case 31:
1274      /*DEL*/ case 127:
1275       c->t->xmit (c->t, &key, 1);
1276       break;
1277
1278     case KEY_UP:
1279     case KEY_DOWN:
1280     case KEY_RIGHT:
1281     case KEY_LEFT:
1282     case KEY_HOME:
1283     case KEY_MIDDLE:
1284     case KEY_END:
1285
1286       if (c->v->private_modes[VT102_PRIVATE_MODE_CURSOR_MODE])
1287         {
1288           uint8_t buf[] = { 033, 'O', 'A' + (key - KEY_UP) };
1289           c->t->xmit (c->t, &buf, sizeof (buf));
1290         }
1291       else
1292         {
1293           uint8_t buf[] = { 033, '[', 'A' + (key - KEY_UP) };
1294           c->t->xmit (c->t, &buf, sizeof (buf));
1295         }
1296       break;
1297     case KEY_STAR:
1298     case KEY_PLUS:
1299     case KEY_COMMA:
1300     case KEY_PERIOD:
1301     case KEY_DIVIDE:
1302     case KEY_0:
1303     case KEY_1:
1304     case KEY_2:
1305     case KEY_3:
1306     case KEY_4:
1307     case KEY_5:
1308     case KEY_6:
1309     case KEY_7:
1310     case KEY_8:
1311     case KEY_9:
1312       if (c->v->application_keypad_mode)
1313         {
1314           uint8_t buf[] = { 033, 'O', 'a' + (key - KEY_154) };
1315           c->t->xmit (c->t, &buf, sizeof (buf));
1316         }
1317       else
1318         {
1319           static char kpoff[KEY_NUM] = {
1320             [KEY_STAR] = '*',
1321             [KEY_PLUS] = '+',
1322             [KEY_COMMA] = ',',
1323             [KEY_MINUS] = '-',
1324             [KEY_PERIOD] = '.',
1325             [KEY_DIVIDE] = '/',
1326             [KEY_0] = '0',
1327             [KEY_1] = '1',
1328             [KEY_2] = '2',
1329             [KEY_3] = '3',
1330             [KEY_4] = '4',
1331             [KEY_5] = '5',
1332             [KEY_6] = '6',
1333             [KEY_7] = '7',
1334             [KEY_8] = '8',
1335             [KEY_9] = '9'
1336           };
1337
1338           c->t->xmit (c->t, &kpoff[key], 1);
1339         }
1340       break;
1341     case KEY_ENTER:
1342       if (c->v->application_keypad_mode)
1343         {
1344           uint8_t buf[] = { 033, 'O', 'M' };
1345           c->t->xmit (c->t, &buf, sizeof (buf));
1346         }
1347       else
1348         {
1349           ch = 13;
1350           c->t->xmit (c->t, &ch, 1);
1351           if (c->v->modes[VT102_MODE_NEWLINE_MODE])
1352             {
1353               ch = 10;
1354               c->t->xmit (c->t, &ch, 1);
1355             }
1356         }
1357       break;
1358     case KEY_PF1:
1359     case KEY_PF2:
1360     case KEY_PF3:
1361     case KEY_PF4:
1362       {
1363         uint8_t buf[] = { 033, 'O', 'P' + (key - KEY_PF1) };
1364         c->t->xmit (c->t, &buf, sizeof (buf));
1365       }
1366       break;
1367     case KEY_INSERT:
1368     case KEY_DELETE:
1369     case KEY_PGUP:
1370     case KEY_PGDN:
1371       {
1372         uint8_t buf[] = { 033, '[', '0' + (key - KEY_180), '~' };
1373         c->t->xmit (c->t, &buf, sizeof (buf));
1374       }
1375       break;
1376     }
1377
1378 }
1379
1380 void
1381 vt102_reset (VT102 * v)
1382 {
1383   VT102_parser *p = &v->parser;
1384
1385   vt102_parser_reset (p);
1386   crt_cls (&v->crt);
1387
1388   v->attr = CRT_ATTR_NORMAL;
1389   v->color = CRT_COLOR_NORMAL;
1390
1391   v->application_keypad_mode = 0;
1392
1393   v->current_line = v->pos;
1394   v->pending_wrap = 0;
1395
1396   v->screen_start.x = 0;
1397   v->screen_start.y = 0;
1398   v->screen_end.x = VT102_COLS - 1;
1399   v->screen_end.y = VT102_ROWS - 1;
1400
1401   v->top_margin = v->screen_start;
1402   v->bottom_margin = v->screen_end;
1403
1404   memset (v->modes, 0, VT102_NMODES);
1405   memset (v->private_modes, 0, VT102_NMODES);
1406
1407   v->private_modes[VT102_PRIVATE_MODE_AUTO_WRAP] = 1;
1408   v->private_modes[VT102_PRIVATE_MODE_AUTO_REPEAT] = 1;
1409   v->private_modes[VT102_PRIVATE_MODE_SHOW_CURSOR] = 1;
1410   v->modes[VT102_MODE_LOCAL_ECHO_OFF] = 1;
1411
1412   vt102_cursor_home (v);
1413   vt102_reset_tabs (v);
1414   v->current_line = v->pos;
1415
1416   vt102_save_state (v);
1417
1418   vt102_status_line (v, "");
1419
1420 }
1421
1422 int
1423 vt102_dispatch (Context * c)
1424 {
1425   char buf[1024];
1426   int red;
1427
1428   red = c->t->recv (c->t, buf, sizeof (buf));
1429
1430   if (red < 0)
1431     return -1;
1432   if (!red)
1433     return 0;
1434
1435
1436   vt102_parse (c, buf, red);
1437
1438   return 0;
1439 }
1440
1441
1442 int
1443 vt102_dispatch_one (Context * c)
1444 {
1445   char buf;
1446   int red;
1447
1448   red = c->t->recv (c->t, &buf, sizeof (buf));
1449
1450   if (red < 0)
1451     return -1;
1452   if (!red)
1453     return 0;
1454
1455   vt102_parse_char (c, buf);
1456
1457   return 0;
1458 }
1459
1460 VT102 *
1461 vt102_new (void)
1462 {
1463   VT102 *v;
1464
1465   v = (VT102 *) malloc (sizeof (VT102));
1466
1467   vt102_reset (v);
1468
1469
1470   return v;
1471 }
1472
1473 void
1474 vt102_free (VT102 * v)
1475 {
1476   free (v);
1477 }