1 /*
2  * %CopyrightBegin%
3  *
4  * Copyright Ericsson AB 1996-2016. All Rights Reserved.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *
18  * %CopyrightEnd%
19  */
20 /*
21  * Tty driver that reads one character at the time and provides a
22  * smart line for output.
23  */
24 
25 #ifdef HAVE_CONFIG_H
26 #  include "config.h"
27 #endif
28 #include "sys.h"
29 #include <ctype.h>
30 #include <stdlib.h>
31 #include <stdio.h>
32 #include <string.h>
33 #include <signal.h>
34 
35 #include "erl_driver.h"
36 #include "win_con.h"
37 
38 #define TRUE 1
39 #define FALSE 0
40 
41 static int cols;		/* Number of columns available. */
42 static int rows;		/* Number of rows available. */
43 
44 /* The various opcodes. */
45 #define OP_PUTC 0
46 #define OP_MOVE 1
47 #define OP_INSC 2
48 #define OP_DELC 3
49 #define OP_BEEP 4
50 #define OP_PUTC_SYNC 5
51 
52 /* Control op */
53 #define CTRL_OP_GET_WINSIZE 100
54 #define CTRL_OP_GET_UNICODE_STATE 101
55 #define CTRL_OP_SET_UNICODE_STATE 102
56 
57 static int lbuf_size = BUFSIZ;
58 Uint32 *lbuf;		/* The current line buffer */
59 int llen;		        /* The current line length */
60 int lpos;                       /* The current "cursor position" in the line buffer */
61 
62 /*
63  * Tags used in line buffer to show that these bytes represent special characters,
64  * Max unicode is 0x0010ffff, so we have lots of place for meta tags...
65  */
66 #define CONTROL_TAG 0x10000000U /* Control character, value in first position */
67 #define ESCAPED_TAG 0x01000000U /* Escaped character, value in first position */
68 #define TAG_MASK    0xFF000000U
69 
70 #define MAXSIZE (1 << 16)
71 
72 #define ISPRINT(c) (isprint(c) || (128+32 <= (c) && (c) < 256))
73 
74 #define DEBUGLOG(X) /* nothing */
75 
76 /*
77  * XXX These are used by win_con.c (for command history).
78  * Should be cleaned up.
79  */
80 
81 
82 #define NL '\n'
83 
84 /* Main interface functions. */
85 static int ttysl_init(void);
86 static ErlDrvData ttysl_start(ErlDrvPort, char*);
87 static void ttysl_stop(ErlDrvData);
88 static ErlDrvSSizeT ttysl_control(ErlDrvData, unsigned int,
89 				  char *, ErlDrvSizeT, char **, ErlDrvSizeT);
90 static void ttysl_from_erlang(ErlDrvData, char*, ErlDrvSizeT);
91 static void ttysl_from_tty(ErlDrvData, ErlDrvEvent);
92 static Sint16 get_sint16(char *s);
93 
94 static ErlDrvPort ttysl_port;
95 
96 extern ErlDrvEvent console_input_event;
97 extern HANDLE console_thread;
98 
99 static HANDLE ttysl_in = INVALID_HANDLE_VALUE; /* Handle for console input. */
100 static HANDLE ttysl_out = INVALID_HANDLE_VALUE;	/* Handle for console output */
101 
102 /* Functions that work on the line buffer. */
103 static int start_lbuf();
104 static int stop_lbuf();
105 static int put_chars();
106 static int move_rel();
107 static int ins_chars();
108 static int del_chars();
109 static int step_over_chars(int n);
110 static int insert_buf();
111 static int write_buf();
112 static void move_cursor(int, int);
113 
114 /* Define the driver table entry. */
115 struct erl_drv_entry ttsl_driver_entry = {
116     ttysl_init,
117     ttysl_start,
118     ttysl_stop,
119     ttysl_from_erlang,
120     ttysl_from_tty,
121     NULL,
122     "tty_sl",
123     NULL,
124     NULL,
125     ttysl_control,
126     NULL, /* timeout */
127     NULL, /* outputv */
128     NULL, /* ready_async */
129     NULL, /* flush */
130     NULL, /* call */
131     NULL, /* event */
132     ERL_DRV_EXTENDED_MARKER,
133     ERL_DRV_EXTENDED_MAJOR_VERSION,
134     ERL_DRV_EXTENDED_MINOR_VERSION,
135     0,
136     NULL,
137     NULL,
138     NULL,
139 };
140 
141 static int utf8_mode = 0;
142 
ttysl_init()143 static int ttysl_init()
144 {
145     lbuf = NULL;		/* For line buffer handling */
146     ttysl_port = (ErlDrvPort)-1;
147     return 0;
148 }
149 
ttysl_start(ErlDrvPort port,char * buf)150 static ErlDrvData ttysl_start(ErlDrvPort port, char* buf)
151 {
152     if ((int)ttysl_port != -1 || console_thread == NULL) {
153 	return ERL_DRV_ERROR_GENERAL;
154     }
155     start_lbuf();
156     utf8_mode = 1;
157     driver_select(port, console_input_event, ERL_DRV_READ, 1);
158     ttysl_port = port;
159     return (ErlDrvData)ttysl_port;/* Nothing important to return */
160 }
161 
162 #define DEF_HEIGHT 24
163 #define DEF_WIDTH 80
164 
ttysl_get_window_size(Uint32 * width,Uint32 * height)165 static void ttysl_get_window_size(Uint32 *width, Uint32 *height)
166 {
167     *width = ConGetColumns();
168     *height = ConGetRows();
169 }
170 
171 
ttysl_control(ErlDrvData drv_data,unsigned int command,char * buf,ErlDrvSizeT len,char ** rbuf,ErlDrvSizeT rlen)172 static ErlDrvSSizeT ttysl_control(ErlDrvData drv_data,
173 			 unsigned int command,
174 			 char *buf, ErlDrvSizeT len,
175 			 char **rbuf, ErlDrvSizeT rlen)
176 {
177     char resbuff[2*sizeof(Uint32)];
178     ErlDrvSizeT res_size;
179 
180     command -= ERTS_TTYSL_DRV_CONTROL_MAGIC_NUMBER;
181     switch (command) {
182     case CTRL_OP_GET_WINSIZE:
183 	{
184 	    Uint32 w,h;
185 	    ttysl_get_window_size(&w,&h);
186 	    memcpy(resbuff,&w,sizeof(Uint32));
187 	    memcpy(resbuff+sizeof(Uint32),&h,sizeof(Uint32));
188 	    res_size = 2*sizeof(Uint32);
189 	}
190 	break;
191     case CTRL_OP_GET_UNICODE_STATE:
192 	*resbuff = (utf8_mode) ? 1 : 0;
193 	res_size = 1;
194 	break;
195     case CTRL_OP_SET_UNICODE_STATE:
196 	if (len != 0) {
197 	    int m = (int) *buf;
198 	    *resbuff = (utf8_mode) ? 1 : 0;
199 	    res_size = 1;
200 	    utf8_mode = (m) ? 1 : 0;
201 	} else {
202 	    return 0;
203 	}
204 	break;
205     default:
206 	return -1;
207     }
208     if (rlen < res_size) {
209 	*rbuf = driver_alloc(res_size);
210     }
211     memcpy(*rbuf,resbuff,res_size);
212     return res_size;
213 }
214 
215 
ttysl_stop(ErlDrvData ttysl_data)216 static void ttysl_stop(ErlDrvData ttysl_data)
217 {
218     if ((int)ttysl_port != -1) {
219         driver_select(ttysl_port, console_input_event, ERL_DRV_READ, 0);
220     }
221 
222     ttysl_in = ttysl_out = INVALID_HANDLE_VALUE;
223     stop_lbuf();
224     ttysl_port = (ErlDrvPort)-1;
225 }
226 
put_utf8(int ch,byte * target,int sz,int * pos)227 static int put_utf8(int ch, byte *target, int sz, int *pos)
228 {
229     Uint x = (Uint) ch;
230     if (x < 0x80) {
231     if (*pos >= sz) {
232 	return -1;
233     }
234 	target[(*pos)++] = (byte) x;
235     }
236     else if (x < 0x800) {
237 	if (((*pos) + 1) >= sz) {
238 	    return -1;
239 	}
240 	target[(*pos)++] = (((byte) (x >> 6)) |
241 			    ((byte) 0xC0));
242 	target[(*pos)++] = (((byte) (x & 0x3F)) |
243 			    ((byte) 0x80));
244     } else if (x < 0x10000) {
245 	if ((x >= 0xD800 && x <= 0xDFFF) ||
246 	    (x == 0xFFFE) ||
247 	    (x == 0xFFFF)) { /* Invalid unicode range */
248 	    return -1;
249 	}
250 	if (((*pos) + 2) >= sz) {
251 	    return -1;
252 	}
253 
254 	target[(*pos)++] = (((byte) (x >> 12)) |
255 			    ((byte) 0xE0));
256 	target[(*pos)++] = ((((byte) (x >> 6)) & 0x3F)  |
257 			    ((byte) 0x80));
258 	target[(*pos)++] = (((byte) (x & 0x3F)) |
259 			    ((byte) 0x80));
260     } else if (x < 0x110000) { /* Standard imposed max */
261 	if (((*pos) + 3) >= sz) {
262 	    return -1;
263 	}
264 	target[(*pos)++] = (((byte) (x >> 18)) |
265 			    ((byte) 0xF0));
266 	target[(*pos)++] = ((((byte) (x >> 12)) & 0x3F)  |
267 			    ((byte) 0x80));
268 	target[(*pos)++] = ((((byte) (x >> 6)) & 0x3F)  |
269 			    ((byte) 0x80));
270 	target[(*pos)++] = (((byte) (x & 0x3F)) |
271 			    ((byte) 0x80));
272     } else {
273 	return -1;
274     }
275     return 0;
276 }
277 
278 
pick_utf8(byte * s,int sz,int * pos)279 static int pick_utf8(byte *s, int sz, int *pos)
280 {
281     int size = sz - (*pos);
282     byte *source;
283     Uint unipoint;
284 
285     if (size > 0) {
286 	source = s + (*pos);
287 	if (((*source) & ((byte) 0x80)) == 0) {
288 	    unipoint = (int) *source;
289 	    ++(*pos);
290 	    return (int) unipoint;
291 	} else if (((*source) & ((byte) 0xE0)) == 0xC0) {
292 	    if (size < 2) {
293 		return -2;
294 	    }
295 	    if (((source[1] & ((byte) 0xC0)) != 0x80) ||
296 		((*source) < 0xC2) /* overlong */) {
297 		return -1;
298 	    }
299 	    (*pos) += 2;
300 	    unipoint =
301 		(((Uint) ((*source) & ((byte) 0x1F))) << 6) |
302 		((Uint) (source[1] & ((byte) 0x3F)));
303 	    return (int) unipoint;
304 	} else if (((*source) & ((byte) 0xF0)) == 0xE0) {
305 	    if (size < 3) {
306 		return -2;
307 	    }
308 	    if (((source[1] & ((byte) 0xC0)) != 0x80) ||
309 		((source[2] & ((byte) 0xC0)) != 0x80) ||
310 		(((*source) == 0xE0) && (source[1] < 0xA0)) /* overlong */ ) {
311 		return -1;
312 	    }
313 	    if ((((*source) & ((byte) 0xF)) == 0xD) &&
314 		((source[1] & 0x20) != 0)) {
315 		return -1;
316 	    }
317 	    if (((*source) == 0xEF) && (source[1] == 0xBF) &&
318 		((source[2] == 0xBE) || (source[2] == 0xBF))) {
319 		return -1;
320 	    }
321 	    (*pos) += 3;
322 	    unipoint =
323 		(((Uint) ((*source) & ((byte) 0xF))) << 12) |
324 		(((Uint) (source[1] & ((byte) 0x3F))) << 6) |
325 		((Uint) (source[2] & ((byte) 0x3F)));
326 	    return (int) unipoint;
327 	} else if (((*source) & ((byte) 0xF8)) == 0xF0) {
328 	    if (size < 4) {
329 		return -2 ;
330 	    }
331 	    if (((source[1] & ((byte) 0xC0)) != 0x80) ||
332 		((source[2] & ((byte) 0xC0)) != 0x80) ||
333 		((source[3] & ((byte) 0xC0)) != 0x80) ||
334 		(((*source) == 0xF0) && (source[1] < 0x90)) /* overlong */) {
335 		return -1;
336 	    }
337 	    if ((((*source) & ((byte)0x7)) > 0x4U) ||
338 		((((*source) & ((byte)0x7)) == 0x4U) &&
339 		 ((source[1] & ((byte)0x3F)) > 0xFU))) {
340 		return -1;
341 	    }
342 	    (*pos) += 4;
343 	    unipoint =
344 		(((Uint) ((*source) & ((byte) 0x7))) << 18) |
345 		(((Uint) (source[1] & ((byte) 0x3F))) << 12) |
346 		(((Uint) (source[2] & ((byte) 0x3F))) << 6) |
347 		((Uint) (source[3] & ((byte) 0x3F)));
348 	    return (int) unipoint;
349 	} else {
350 	    return -1;
351 	}
352     } else {
353 	return -1;
354     }
355 }
356 
octal_or_hex_positions(Uint c)357 static int octal_or_hex_positions(Uint c)
358 {
359     int x = 0;
360     Uint ch = c;
361     if (!ch) {
362 	return 1;
363     }
364     while(ch) {
365 	++x;
366 	ch >>= 3;
367     }
368     if (x <= 3) {
369 	return 3;
370     }
371     /* \x{H ...} format when larger than \777 */
372     x = 0;
373     ch = c;
374     while(ch) {
375 	++x;
376 	ch >>= 4;
377     }
378     return x+3;
379 }
380 
octal_or_hex_format(Uint ch,byte * buf,int * pos)381 static void octal_or_hex_format(Uint ch, byte *buf, int *pos)
382 {
383     static byte hex_chars[] = { '0','1','2','3','4','5','6','7','8','9',
384 				'A','B','C','D','E','F'};
385     int num = octal_or_hex_positions(ch);
386     if (num != 3) {
387 	buf[(*pos)++] = 'x';
388 	buf[(*pos)++] = '{';
389 	num -= 3;
390 	while(num--) {
391 	    buf[(*pos)++] = hex_chars[((ch >> (4*num)) & 0xFU)];
392 	}
393 	buf[(*pos)++] = '}';
394     } else {
395 	while(num--) {
396 	    buf[(*pos)++] = ((byte) ((ch >> (3*num)) & 0x7U) + '0');
397 	}
398     }
399 }
400 
401 /*
402  * Check that there is enough room in all buffers to copy all pad chars
403  * and stiff we need If not, realloc lbuf.
404  */
check_buf_size(byte * s,int n)405 static int check_buf_size(byte *s, int n)
406 {
407     int pos = 0;
408     int ch;
409     int size = 10;
410 
411     while(pos < n) {
412 	/* Indata is always UTF-8 */
413 	if ((ch = pick_utf8(s,n,&pos)) < 0) {
414 	    /* XXX temporary allow invalid chars */
415 	    ch = (int) s[pos];
416 	    DEBUGLOG(("Invalid UTF8:%d",ch));
417 	    ++pos;
418 	}
419 	if (utf8_mode) { /* That is, terminal is UTF8 compliant */
420 	    if (ch >= 128 || isprint(ch)) {
421 		DEBUGLOG(("Printable(UTF-8:%d):%d",pos,ch));
422 		size++; /* Buffer contains wide characters... */
423 	    } else if (ch == '\t') {
424 		size += 8;
425 	    } else {
426 		DEBUGLOG(("Magic(UTF-8:%d):%d",pos,ch));
427 		size += 2;
428 	    }
429 	} else {
430 	    if (ch <= 255 && isprint(ch)) {
431 		DEBUGLOG(("Printable:%d",ch));
432 		size++;
433 	    } else if (ch == '\t')
434 		size += 8;
435 	    else if (ch >= 128) {
436 		DEBUGLOG(("Non printable:%d",ch));
437 		size += (octal_or_hex_positions(ch) + 1);
438 	    }
439 	    else {
440 		DEBUGLOG(("Magic:%d",ch));
441 		size += 2;
442 	    }
443 	}
444     }
445 
446     if (size + lpos >= lbuf_size) {
447 
448 	lbuf_size = size + lpos + BUFSIZ;
449 	if ((lbuf = driver_realloc(lbuf, lbuf_size * sizeof(Uint32))) == NULL) {
450 	    driver_failure(ttysl_port, -1);
451 	    return(0);
452 	}
453     }
454     return(1);
455 }
456 
457 
ttysl_from_erlang(ErlDrvData ttysl_data,char * buf,ErlDrvSizeT count)458 static void ttysl_from_erlang(ErlDrvData ttysl_data, char* buf, ErlDrvSizeT count)
459 {
460     if (lpos > MAXSIZE)
461 	put_chars((byte*)"\n", 1);
462 
463     switch (buf[0]) {
464     case OP_PUTC:
465     case OP_PUTC_SYNC:
466 	DEBUGLOG(("OP: Putc(%I64u)",(unsigned long long)count-1));
467 	if (check_buf_size((byte*)buf+1, count-1) == 0)
468 	    return;
469 	put_chars((byte*)buf+1, count-1);
470 	break;
471     case OP_MOVE:
472 	move_rel(get_sint16(buf+1));
473 	break;
474     case OP_INSC:
475 	if (check_buf_size((byte*)buf+1, count-1) == 0)
476 	    return;
477 	ins_chars((byte*)buf+1, count-1);
478 	break;
479     case OP_DELC:
480 	del_chars(get_sint16(buf+1));
481 	break;
482     case OP_BEEP:
483 	ConBeep();
484 	break;
485     default:
486 	/* Unknown op, just ignore. */
487 	break;
488     }
489 
490     if (buf[0] == OP_PUTC_SYNC) {
491         /* On windows we do a blocking write to the tty so we just
492            send the ack immidiately. If at some point in the future
493            someone has a problem with tty output being blocking
494            this has to be changed. */
495         ErlDrvTermData spec[] = {
496             ERL_DRV_PORT, driver_mk_port(ttysl_port),
497             ERL_DRV_ATOM, driver_mk_atom("ok"),
498             ERL_DRV_TUPLE, 2
499         };
500         erl_drv_output_term(driver_mk_port(ttysl_port), spec,
501                             sizeof(spec) / sizeof(spec[0]));
502     }
503     return;
504 }
505 
506 extern int read_inbuf(char *data, int n);
507 
ttysl_from_tty(ErlDrvData ttysl_data,ErlDrvEvent fd)508 static void ttysl_from_tty(ErlDrvData ttysl_data, ErlDrvEvent fd)
509 {
510    Uint32 inbuf[64];
511    byte t[1024];
512    int i,pos,tpos;
513 
514    i = ConReadInput(inbuf,1);
515 
516    pos = 0;
517    tpos = 0;
518 
519    while (pos < i) {
520        while (tpos < 1020 && pos < i) { /* Max 4 bytes for UTF8 */
521 	   put_utf8((int) inbuf[pos++], t, 1024, &tpos);
522        }
523        driver_output(ttysl_port, (char *) t, tpos);
524        tpos = 0;
525    }
526 }
527 
528 /*
529  * Gets signed 16 bit integer from binary buffer.
530  */
531 static Sint16
get_sint16(char * s)532 get_sint16(char *s)
533 {
534     return ((*s << 8) | ((byte*)s)[1]);
535 }
536 
537 
start_lbuf(void)538 static int start_lbuf(void)
539 {
540     if (!lbuf && !(lbuf = ( Uint32*) driver_alloc(lbuf_size * sizeof(Uint32))))
541       return FALSE;
542     llen = 0;
543     lpos = 0;
544     return TRUE;
545 }
546 
stop_lbuf(void)547 static int stop_lbuf(void)
548 {
549     if (lbuf) {
550 	driver_free(lbuf);
551 	lbuf = NULL;
552     }
553     llen = 0; /* To avoid access error in win_con:AddToCmdHistory during exit*/
554     return TRUE;
555 }
556 
557 /* Put l bytes (in UTF8) from s into the buffer and output them. */
put_chars(byte * s,int l)558 static int put_chars(byte *s, int l)
559 {
560     int n;
561 
562     n = insert_buf(s, l);
563     if (n > 0)
564       write_buf(lbuf + lpos - n, n);
565     if (lpos > llen)
566       llen = lpos;
567     return TRUE;
568 }
569 
570 /*
571  * Move the current postition forwards or backwards within the current
572  * line. We know about padding.
573  */
move_rel(int n)574 static int move_rel(int n)
575 {
576     int npos;			/* The new position */
577 
578     /* Step forwards or backwards over the buffer. */
579     npos = step_over_chars(n);
580 
581     /* Calculate move, updates pointers and move the cursor. */
582     move_cursor(lpos, npos);
583     lpos = npos;
584     return TRUE;
585 }
586 
587 /* Insert characters into the buffer at the current position. */
ins_chars(byte * s,int l)588 static int ins_chars(byte *s, int l)
589 {
590     int n, tl;
591     Uint32 *tbuf = NULL;    /* Suppress warning about use-before-set */
592 
593     /* Move tail of buffer to make space. */
594     if ((tl = llen - lpos) > 0) {
595 	if ((tbuf = driver_alloc(tl * sizeof(Uint32))) == NULL)
596 	    return FALSE;
597 	memcpy(tbuf, lbuf + lpos, tl * sizeof(Uint32));
598     }
599     n = insert_buf(s, l);
600     if (tl > 0) {
601 	memcpy(lbuf + lpos, tbuf, tl * sizeof(Uint32));
602 	driver_free(tbuf);
603     }
604     llen += n;
605     write_buf(lbuf + (lpos - n), llen - (lpos - n));
606     move_cursor(llen, lpos);
607     return TRUE;
608 }
609 
610 /*
611  * Delete characters in the buffer. Can delete characters before (n < 0)
612  * and after (n > 0) the current position. Cursor left at beginning of
613  * deleted block.
614  */
del_chars(int n)615 static int del_chars(int n)
616 {
617     int i, l, r;
618     int pos;
619 
620     /*update_cols();*/
621 
622     /* Step forward or backwards over n logical characters. */
623     pos = step_over_chars(n);
624 
625     if (pos > lpos) {
626 	l = pos - lpos;		/* Buffer characters to delete */
627 	r = llen - lpos - l;	/* Characters after deleted */
628 	/* Fix up buffer and buffer pointers. */
629 	if (r > 0)
630 	    memcpy(lbuf + lpos, lbuf + pos, r * sizeof(Uint32));
631 	llen -= l;
632 	/* Write out characters after, blank the tail and jump back to lpos. */
633 	write_buf(lbuf + lpos, r);
634 	for (i = l ; i > 0; --i)
635 	  ConPutChar(' ');
636 	move_cursor(llen + l, lpos);
637     }
638     else if (pos < lpos) {
639 	l = lpos - pos;		/* Buffer characters */
640 	r = llen - lpos;	/* Characters after deleted */
641 	move_cursor(lpos, lpos-l);	/* Move back */
642 	/* Fix up buffer and buffer pointers. */
643 	if (r > 0)
644 	    memcpy(lbuf + pos, lbuf + lpos, r * sizeof(Uint32));
645 	lpos -= l;
646 	llen -= l;
647 	/* Write out characters after, blank the tail and jump back to lpos. */
648 	write_buf(lbuf + lpos, r);
649 	for (i = l ; i > 0; --i)
650 	  ConPutChar(' ');
651 	move_cursor(llen + l, lpos);
652     }
653     return TRUE;
654 }
655 
656 
657 /* Step over n logical characters, check for overflow. */
step_over_chars(int n)658 static int step_over_chars(int n)
659 {
660     Uint32 *c, *beg, *end;
661 
662     beg = lbuf;
663     end = lbuf + llen;
664     c = lbuf + lpos;
665     for ( ; n > 0 && c < end; --n) {
666 	c++;
667 	while (c < end && (*c & TAG_MASK) && ((*c & ~TAG_MASK) == 0))
668 	    c++;
669     }
670     for ( ; n < 0 && c > beg; n++) {
671 	--c;
672 	while (c > beg && (*c & TAG_MASK) && ((*c & ~TAG_MASK) == 0))
673 	    --c;
674     }
675     return c - lbuf;
676 }
677 
insert_buf(byte * s,int n)678 static int insert_buf(byte *s, int n)
679 {
680     int pos = 0;
681     int buffpos = lpos;
682     int ch;
683 
684     while (pos < n) {
685 	if ((ch = pick_utf8(s,n,&pos)) < 0) {
686 	    /* XXX temporary allow invalid chars */
687 	    ch = (int) s[pos];
688 	    DEBUGLOG(("insert_buf: Invalid UTF8:%d",ch));
689 	    ++pos;
690 	}
691 	if ((utf8_mode && (ch >= 128 || isprint(ch))) || (ch <= 255 && isprint(ch))) {
692 	    DEBUGLOG(("insert_buf: Printable(UTF-8):%d",ch));
693 	    lbuf[lpos++] = (Uint32) ch;
694 	} else if (ch >= 128) { /* not utf8 mode */
695 	    int nc = octal_or_hex_positions(ch);
696 	    lbuf[lpos++] = ((Uint32) ch) | ESCAPED_TAG;
697 	    while (nc--) {
698 		lbuf[lpos++] = ESCAPED_TAG;
699 	    }
700 	} else if (ch == '\t') {
701 	    do {
702 		lbuf[lpos++] = (CONTROL_TAG | ((Uint32) ch));
703 		ch = 0;
704 	    } while (lpos % 8);
705 	} else if (ch == '\n' || ch == '\r') {
706 	    write_buf(lbuf + buffpos, lpos - buffpos);
707 	    ConPutChar('\r');
708 	    if (ch == '\n')
709 		ConPutChar('\n');
710 	    if (llen > lpos) {
711 		memcpy(lbuf, lbuf + lpos, llen - lpos);
712 	    }
713 	    llen -= lpos;
714 	    lpos = buffpos = 0;
715 	} else {
716 	    DEBUGLOG(("insert_buf: Magic(UTF-8):%d",ch));
717 	    lbuf[lpos++] = ch | CONTROL_TAG;
718 	    lbuf[lpos++] = CONTROL_TAG;
719 	}
720     }
721     return lpos - buffpos; /* characters "written" into
722 			      current buffer (may be less due to newline) */
723 }
write_buf(Uint32 * s,int n)724 static int write_buf(Uint32 *s, int n)
725 {
726     int i;
727 
728     /*update_cols();*/
729 
730     while (n > 0) {
731 	if (!(*s & TAG_MASK) ) {
732 	    ConPutChar(*s);
733 	    --n;
734 	    ++s;
735 	}
736 	else if (*s == (CONTROL_TAG | ((Uint32) '\t'))) {
737 	    ConPutChar(' ');
738 	    --n; s++;
739 	    while (n > 0 && *s == CONTROL_TAG) {
740 		ConPutChar(' ');
741 		--n; s++;
742 	    }
743 	} else if (*s & CONTROL_TAG) {
744 	    ConPutChar('^');
745 	    ConPutChar((*s == 0177) ? '?' : *s | 0x40);
746 	    n -= 2;
747 	    s += 2;
748 	} else if (*s & ESCAPED_TAG) {
749 	    Uint32 ch = *s & ~(TAG_MASK);
750 	    byte *octbuff;
751 	    byte octtmp[256];
752 	    int octbytes;
753 	    DEBUGLOG(("Escaped: %d", ch));
754 	    octbytes = octal_or_hex_positions(ch);
755 	    if (octbytes > 256) {
756 		octbuff = driver_alloc(octbytes);
757 	    } else {
758 		octbuff = octtmp;
759 	    }
760 	    octbytes = 0;
761 	    octal_or_hex_format(ch, octbuff, &octbytes);
762 	     DEBUGLOG(("octbytes: %d", octbytes));
763 	    ConPutChar('\\');
764 	    for (i = 0; i < octbytes; ++i) {
765 		ConPutChar(octbuff[i]);
766 	    }
767 	    n -= octbytes+1;
768 	    s += octbytes+1;
769 	    if (octbuff != octtmp) {
770 		driver_free(octbuff);
771 	    }
772 	} else {
773 	    DEBUGLOG(("Very unexpected character %d",(int) *s));
774 	    ++n;
775 	    --s;
776 	}
777     }
778     return TRUE;
779 }
780 
781 
782 static void
move_cursor(int from,int to)783 move_cursor(int from, int to)
784 {
785     ConSetCursor(from,to);
786 }
787