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