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