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