1 /*
2  *  R : A Computer Language for Statistical Data Analysis
3  *  file console.c
4  *  Copyright (C) 1998--2003  Guido Masarotto and Brian Ripley
5  *  Copyright (C) 2004-8      The R Foundation
6  *  Copyright (C) 2004-2020   The R Core Team
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation; either version 2 of the License, or
11  *  (at your option) any later version.
12  *
13  *  This program is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with this program; if not, a copy is available at
20  *  https://www.R-project.org/Licenses/
21  */
22 
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26 
27 #include "win-nls.h"
28 #include <R_ext/Boolean.h>
29 extern Rboolean mbcslocale;
30 
31 #define USE_MDI 1
32 extern void R_ProcessEvents(void);
33 extern void R_WaitEvent(void);
34 
35 #define WIN32_LEAN_AND_MEAN 1
36 #include <windows.h>
37 #include <string.h>
38 #include <ctype.h>
39 #include <wchar.h>
40 #include <limits.h>
41 #include <rlocale.h>
42 #include <R_ext/Memory.h>
43 #include "graphapp/ga.h"
44 #ifdef USE_MDI
45 #include "graphapp/stdimg.h"
46 #endif
47 #include "console.h"
48 #include "consolestructs.h"
49 #include "rui.h"
50 #include "getline/wc_history.h"
51 #include "Startup.h" /* for CharacterMode */
52 #include <Fileio.h>
53 
54 #include <stdint.h>
55 
56 /* Surrogate Pairs Macro */
57 #define SURROGATE_PAIRS_HI_MIN  ((uint16_t)0xd800)
58 #define SURROGATE_PAIRS_HI_MAX  ((uint16_t)0xdbff)
59 #define SURROGATE_PAIRS_LO_MIN  ((uint16_t)0xdc00)
60 #define SURROGATE_PAIRS_LO_MAX  ((uint16_t)0xdfff)
61 #define SURROGATE_PAIRS_BIT_SZ  ((uint32_t)10)
62 #define SURROGATE_PAIRS_MASK    (((uint16_t)1 << SURROGATE_PAIRS_BIT_SZ)-1)
63 #define IsSurrogatePairsHi(_h)  (SURROGATE_PAIRS_HI_MIN == \
64 		      ((uint16_t)(_h) &~ (uint16_t)SURROGATE_PAIRS_MASK ))
65 #define IsSurrogatePairsLo(_l)  (SURROGATE_PAIRS_LO_MIN == \
66 		      ((uint16_t)(_l) &~ (uint16_t)SURROGATE_PAIRS_MASK ))
67 
68 #ifdef __GNUC__
69 # undef alloca
70 # define alloca(x) __builtin_alloca((x))
71 #endif
72 
73 static void performCompletion(control c);
74 
75 
wcswidth(const wchar_t * s)76 static inline int wcswidth(const wchar_t *s)
77 {
78     return mbcslocale ? Ri18n_wcswidth(s, wcslen(s)) : wcslen(s);
79 }
80 
wcwidth(const wchar_t s)81 static inline int wcwidth(const wchar_t s)
82 {
83     return mbcslocale ? Ri18n_wcwidth(s) : 1;
84 }
85 
setCURCOL(ConsoleData p)86 static void setCURCOL(ConsoleData p)
87 {
88     wchar_t *P = LINE(NUMLINES - 1);
89     int w0 = 0;
90 
91     for (; P < LINE(NUMLINES - 1) + prompt_len + cur_pos; P++)
92 	if(*P == L'\r') w0 = 0; else w0 += wcwidth(*P);
93 
94     CURCOL = w0;
95 }
96 
97 
98 /* xbuf */
99 
newxbuf(xlong dim,xint ms,xint shift)100 xbuf newxbuf(xlong dim, xint ms, xint shift)
101 {
102     xbuf  p;
103 
104     p = (xbuf) malloc(sizeof(struct structXBUF));
105     if (!p)
106 	return NULL;
107     p->b = (wchar_t *) malloc((dim + 1) * sizeof(wchar_t));
108     if (!p->b) {
109 	free(p);
110 	return NULL;
111     }
112     p->user = (int *) malloc(ms * sizeof(int));
113     if (!p->user) {
114 	free(p->b);
115 	free(p);
116 	return NULL;
117     }
118     p->s = (wchar_t **) malloc(ms * sizeof(wchar_t *));
119     if (!p->s) {
120 	free(p->b);
121 	free(p->user);
122 	free(p);
123 	return NULL;
124     }
125     p->ns = 1;
126     p->ms = ms;
127     p->shift = shift;
128     p->dim = dim;
129     p->av = dim;
130     p->free = p->b;
131     p->s[0] = p->b;
132     p->user[0] = -1;
133     *p->b = L'\0';
134     return p;
135 }
136 
137 /* reallocate increased buffer sizes and update pointers */
xbufgrow(xbuf p,xlong dim,xint ms)138 void xbufgrow(xbuf p, xlong dim, xint ms)
139 {
140     if(dim > p->dim) {
141 	wchar_t *ret = (wchar_t *) realloc(p->b, (dim + 1)*sizeof(wchar_t));
142 	if(ret) {
143 	    int i, change;
144 	    change = ret - p->b;
145 	    p->b = ret;
146 	    p->av += change;
147 	    p->free += change;
148 	    for (i = 0; i < p->ns; i++)  p->s[i] += change;
149 	    p->dim = dim;
150 	}
151     }
152     if(ms > p->ms) {
153 	wchar_t **ret = (wchar_t **) realloc(p->s, ms * sizeof(wchar_t *));
154 	if(ret) {
155 	    int *ret2 = (int *) realloc(p->user, ms * sizeof(int));
156 	    if(ret2) {
157 		p->s = ret;
158 		p->user = ret2;
159 		p->ms = ms;
160 	    }
161 	}
162     }
163 }
164 
xbufdel(xbuf p)165 void xbufdel(xbuf p)
166 {
167    if (!p) return;
168    free(p->s);
169    free(p->b);
170    free(p->user);
171    free(p);
172 }
173 
xbufshift(xbuf p)174 static void xbufshift(xbuf p)
175 {
176     xint  i;
177     xlong mshift;
178     wchar_t *new0;
179 
180     if (p->shift >= p->ns) {
181 	p->ns = 1;
182 	p->av = p->dim;
183 	p->free = p->b;
184 	p->s[0] = p->b;
185 	*p->b = L'\0';
186 	p->user[0] = -1;
187 	return;
188     }
189     new0 = p->s[p->shift];
190     mshift = new0 - p->s[0];
191     memmove(p->b, p->s[p->shift], (p->dim - mshift) * sizeof(wchar_t));
192     memmove(p->user, &p->user[p->shift], (p->ms - p->shift) * sizeof(int));
193     for (i = p->shift; i < p->ns; i++)
194 	p->s[i - p->shift] = p->s[i] - mshift;
195     p->ns = p->ns - p->shift;
196     p->free -= mshift;
197     p->av += mshift;
198 }
199 
xbufmakeroom(xbuf p,xlong size)200 static int xbufmakeroom(xbuf p, xlong size)
201 {
202     if (size > p->dim) return 0;
203     while ((p->av < size) || (p->ns == p->ms)) {
204 	xbufshift(p);
205     }
206     p->av -= size;
207     return 1;
208 }
209 
210 #define XPUTC(c) {xbufmakeroom(p,1); *p->free++=c;}
211 
xbufaddxc(xbuf p,wchar_t c)212 void xbufaddxc(xbuf p, wchar_t c)
213 {
214     int   i;
215 
216     switch (c) {
217     case L'\a':
218 	gabeep();
219 	break;
220     case L'\b':
221 	if ((p->s[p->ns - 1])[0]) {
222 	    p->free--;
223 	    p->av++;
224 	}
225 	break;
226     case L'\t':
227 	XPUTC(L' ');
228 	*p->free = '\0';
229 	/* Changed to  width in 2.7.0 */
230 	for (i = wcswidth(p->s[p->ns - 1]); (i % TABSIZE); i++) XPUTC(L' ');
231 	break;
232     case L'\n':
233 	XPUTC(L'\0');
234 	p->s[p->ns] = p->free;
235 	p->user[p->ns++] = -1;
236 	break;
237     default:
238 	XPUTC(c);
239     }
240     *p->free = L'\0';
241 }
242 
xbufaddxs(xbuf p,const wchar_t * s,int user)243 void xbufaddxs(xbuf p, const wchar_t *s, int user)
244 {
245     const wchar_t *ps;
246     int   l;
247 
248     l = user ? (p->s[p->ns - 1])[0] : -1;
249     for (ps = s; *ps; ps++) xbufaddxc(p, *ps);
250     p->user[p->ns - 1] = l;
251 }
252 
253 #define IN_CONSOLE
254 #include "rgui_UTF8.h"
255 extern size_t Rf_utf8towcs(wchar_t *wc, const char *s, size_t n);
enctowcs(wchar_t * wc,char * s,int n)256 static size_t enctowcs(wchar_t *wc, char *s, int n)
257 {
258     size_t nc = 0;
259     char *pb, *pe;
260     if((pb = strchr(s, UTF8in[0])) && *(pb+1) == UTF8in[1] &&
261        *(pb+2) == UTF8in[2]) {
262 	*pb = '\0';
263 	nc += mbstowcs(wc, s, n);
264 	*pb = UTF8in[0]; /* preserve input string, needed in consolewrites */
265 	pb += 3; pe = pb;
266 	while(*pe &&
267 	      !((pe = strchr(pe, UTF8out[0])) && *(pe+1) == UTF8out[1] &&
268 	      *(pe+2) == UTF8out[2])) pe++;
269 	if(!*pe) return nc; /* FIXME */;
270 	*pe = '\0';
271 	/* convert string starting at pb from UTF-8 */
272 	nc += Rf_utf8towcs(wc+nc, pb, (pe-pb));
273 	*pe = UTF8out[0]; /* preserve input string, needed in consolewrites */
274 	pe += 3;
275 	nc += enctowcs(wc+nc, pe, n-nc);
276     } else nc = mbstowcs(wc, s, n);
277     return nc;
278 }
279 
xbufadds(xbuf p,const char * s,int user)280 static void xbufadds(xbuf p, const char *s, int user)
281 {
282     int n = strlen(s) + 1; /* UCS-2 must be no more chars */
283     if (n < 1000) {
284 	wchar_t tmp[n];
285 	enctowcs(tmp, (char *) s, n);
286 	xbufaddxs(p, tmp, user);
287     } else {
288 	/* very long line */
289 	wchar_t *tmp = (wchar_t*) malloc(n * sizeof(wchar_t));
290 	enctowcs(tmp, (char *) s, n);
291 	xbufaddxs(p, tmp, user);
292 	free(tmp);
293     }
294 }
295 
xbuffixl(xbuf p)296 static void xbuffixl(xbuf p)
297 {
298     wchar_t *ps;
299 
300     if (!p->ns) return;
301     ps = p->s[p->ns - 1];
302     p->free = ps + wcslen(ps);
303     p->av = p->dim - (p->free - p->b);
304 }
305 
306 
307 /* console */
308 
309 rgb guiColors[numGuiColors] = {
310 	White, Black, gaRed, /* consolebg, consolefg, consoleuser, */
311 	White, Black, gaRed, /* pagerbg, pagerfg, pagerhighlight,  */
312 	White, Black, gaRed, /* dataeditbg, dataeditfg, dataedituser */
313 	White, Black         /* editorbg, editorfg                 */
314 };
315 
316 extern int R_HistorySize;  /* from Defn.h */
317 
318 ConsoleData
newconsoledata(font f,int rows,int cols,int bufbytes,int buflines,rgb * guiColors,int kind,int buffered,int cursor_blink)319 newconsoledata(font f, int rows, int cols, int bufbytes, int buflines,
320 	       rgb *guiColors, int kind, int buffered, int cursor_blink)
321 {
322     ConsoleData p;
323 
324     initapp(0, 0);
325     p = (ConsoleData) malloc(sizeof(struct structConsoleData));
326     if (!p)
327 	return NULL;
328     p->kind = kind;
329     /* PR#14624 claimed this was needed, with no example */
330     p->chbrk = p->modbrk = '\0';
331     if (kind == CONSOLE) {
332 	p->lbuf = newxbuf(bufbytes, buflines, SLBUF);
333 	if (!p->lbuf) {
334 	    free(p);
335 	    return NULL;
336 	}
337 	p->kbuf = malloc(NKEYS * sizeof(wchar_t));
338 	if (!p->kbuf) {
339 	    xbufdel(p->lbuf);
340 	    free(p);
341 	    return NULL;
342 	}
343     } else {
344 	p->lbuf = NULL;
345 	p->kbuf = NULL;
346     }
347     BM = NULL;
348     p->rows = rows;
349     p->cols = cols;
350     for (int i=0; i<numGuiColors; i++)
351 	p->guiColors[i] = guiColors[i];
352     p->f = f;
353     FH = fontheight(f);
354     FW = fontwidth(f);
355     WIDTH = (COLS + 1) * FW;
356     HEIGHT = (ROWS + 1) * FH + 1; /* +1 avoids size problems in MDI */
357     FV = FC = 0;
358     NEWFV = NEWFC = 0;
359     p->firstkey = p->numkeys = 0;
360     p->clp = NULL;
361     CURROW = -1;
362     p->overwrite = 0;
363     p->needredraw = 0;
364     p->wipe_completion = 0;
365     p->my0 = p->my1 = -1;
366     p->mx0 = 5;
367     p->mx1 = 14;
368     p->sel = 0;
369     p->input = 0;
370     p->lazyupdate = buffered;
371     p->cursor_blink = cursor_blink;
372     return (p);
373 }
374 
col_to_pos(ConsoleData p,int x)375 static int col_to_pos(ConsoleData p, int x)
376 {
377     if(mbcslocale) {
378 	int w0 = 0, cnt = 0;
379 	wchar_t *P = LINE(CURROW);
380 	for (; w0 < x && *P && P - LINE(CURROW) <= max_pos + prompt_len; ) {
381 	    w0 += Ri18n_wcwidth(*P++);
382 	    cnt++;
383 	}
384 	return(cnt - prompt_len);
385     } else
386 	return(min(x - prompt_len, max_pos));
387 }
388 
within_input(ConsoleData p,int mx,int my)389 static int within_input(ConsoleData p, int mx, int my)
390 {
391     return(my == CURROW && mx >= prompt_wid && col_to_pos(p, mx) < max_pos);
392 }
393 
394 /* Intersect the mouse selection with the input region. If no overlap or !apply, do nothing*/
intersect_input(ConsoleData p,int apply)395 static int intersect_input(ConsoleData p, int apply)
396 {
397     int my0 = p->my0, my1 = p->my1, mx0 = p->mx0, mx1 = p->mx1, temp;
398     if (my0 > my1 || (my0 == my1 && mx0 > my1)) { /* put them in order */
399 	temp = my0;
400 	my0 = my1;
401 	my1 = temp;
402 	temp = mx0;
403 	mx0 = mx1;
404 	mx1 = temp;
405     }
406 
407     if (my1 < CURROW || my0 > CURROW) return(0);
408     if (my0 < CURROW) mx0 = 0;
409     if (my1 > CURROW) mx1 = COLS;
410     if (mx1 < CURCOL || col_to_pos(p, mx0) >= max_pos) return(0);
411     mx0 = max(mx0, prompt_wid);
412     while (col_to_pos(p, mx1) >= max_pos) mx1--;
413     if (apply) {
414 	p->mx0 = mx0;
415 	p->mx1 = mx1;
416 	p->my0 = my0;
417 	p->my1 = my1;
418     }
419     return(1);
420 }
421 
422 /* Here fch and lch are columns, and we have to cope with both MBCS
423    and double-width chars. */
424 
writelineHelper(ConsoleData p,int fch,int lch,rgb fgr,rgb bgr,int j,int len,wchar_t * s)425 static void writelineHelper(ConsoleData p, int fch, int lch,
426 			    rgb fgr, rgb bgr, int j, int len, wchar_t *s)
427 {
428     rect  r;
429 
430     /* This is right, since columns are of fixed size */
431     r = rect(BORDERX + fch * FW, BORDERY + j * FH, (lch - fch + 1) * FW, FH);
432     gfillrect(BM, bgr, r);
433 
434     if (len > FC+fch) {
435 	/* Some of the string is visible: */
436 	if(mbcslocale) {
437 	    int i, w0, nc;
438 	    wchar_t *P = s, *q;
439 	    Rboolean leftedge;
440 
441 	    nc = (wcslen(s) + 1) * sizeof(wchar_t); /* overkill */
442 	    wchar_t *buff = (wchar_t*) R_alloc(nc, sizeof(wchar_t));
443 	    q = buff;
444 	    leftedge = FC && (fch == 0);
445 	    if(leftedge) fch++;
446 	    for (w0 = -FC; w0 < fch && *P; P++) /* should have enough ... */
447 		w0 += Ri18n_wcwidth(*P);
448 	    /* Now we have got to on or just after the left edge.
449 	       Possibly have a widechar hanging over.
450 	       If so, fill with blanks.
451 	    */
452 	    if(w0 > fch) for(i = 0; i < w0 - fch; i++) *q++ = L' ';
453 
454 	    if (leftedge) *q++ = L'$';
455 
456 	    while (w0 < lch) {
457 		if(!*P) break;
458 		w0 += Ri18n_wcwidth(*P);
459 		if(w0 > lch) break; /* char straddling the right edge
460 				       is not displayed */
461 		*q++ = *P++;
462 	    }
463 	    if((len > FC+COLS) && (lch == COLS - 1)) *q++ = L'$';
464 	    else *q++ = *P++;
465 	    *q = L'\0';
466 	    gdrawwcs(BM, p->f, fgr, pt(r.x, r.y), buff);
467 	} else {
468 	    int last;
469 	    wchar_t ch, chf, chl;
470 	    /* we don't know the string length, so modify it in place */
471 	    if (FC && (fch == 0)) {chf = s[FC]; s[FC] = '$';} else chf = L'\0';
472 	    if ((len > FC+COLS) && (lch == COLS - 1)) {
473 		chl = s[FC+lch]; s[FC+lch] = '$';
474 	    } else chl = L'\0';
475 	    last = FC + lch + 1;
476 	    if (len > last) {ch = s[last]; s[last] = L'\0';} else ch = L'\0';
477 	    gdrawwcs(BM, p->f, fgr, pt(r.x, r.y), &s[FC+fch]);
478 	    /* restore the string */
479 	    if (ch) s[last] = ch;
480 	    if (chl) s[FC+lch] = chl;
481 	    if (chf) s[FC] = chf;
482 	}
483     }
484 }
485 
486 #define WLHELPER(a, b, c, d) writelineHelper(p, a, b, c, d, j, len, s)
487 
488 /* write line i of the buffer at row j on bitmap */
writeline(control c,ConsoleData p,int i,int j)489 static int writeline(control c, ConsoleData p, int i, int j)
490 {
491     wchar_t *s, *stmp, *p0;
492     int   insel, len, col1, d;
493     int   c1, c2, c3, x0, y0, x1, y1;
494     rect r;
495     int   bg, fg, highlight, base;
496 
497     if (p->kind == CONSOLE) base = consolebg;
498     else if (p->kind == PAGER) base = pagerbg;
499     else base = dataeditbg;
500 
501     bg = p->guiColors[base];
502     fg = p->guiColors[base+1];
503     highlight = p->guiColors[base+2];
504 
505     if ((i < 0) || (i >= NUMLINES)) return 0;
506     stmp = s = LINE(i);
507     len = wcswidth(stmp);
508     /* If there is a \r in the line, we need to preprocess it */
509     if((p0 = wcschr(s, L'\r'))) {
510 	int l, l1;
511 	stmp = LINE(i);
512 	s = (wchar_t *) alloca((wcslen(stmp) + 1) * sizeof(wchar_t));
513 	l = p0 - stmp;
514 	wcsncpy(s, stmp, l);
515 	stmp = p0 + 1;
516 	while((p0 = wcschr(stmp, L'\r'))) {
517 	    l1 = p0 - stmp;
518 	    wcsncpy(s, stmp, l1);
519 	    if(l1 > l) l = l1;
520 	    stmp = p0 + 1;
521 	}
522 	l1 = wcslen(stmp);
523 	wcsncpy(s, stmp, l1);
524 	if(l1 > l) l = l1;
525 	s[l] = L'\0';
526 	len = l; /* for redraw that uses len */
527 	/* and reset cursor position */
528 	{
529 	    wchar_t *P = s;
530 	    int w0;
531 	    for (w0 = 0; *P; P++) w0 += wcwidth(*P);
532 	    CURCOL = w0;
533 	}
534     }
535     col1 = COLS - 1;
536     insel = p->sel ? ((i - p->my0) * (i - p->my1)) : 1;
537     if (insel < 0) {
538 	WLHELPER(0, col1, bg, fg);
539 	return len;
540     }
541     if ((USER(i) >= 0) && (USER(i) < FC + COLS)) {
542 	if (USER(i) <= FC)
543 	    WLHELPER(0, col1, highlight, bg);
544 	else {
545 	    d = USER(i) - FC;
546 	    WLHELPER(0, d - 1, fg, bg);
547 	    WLHELPER(d, col1, highlight, bg);
548 	}
549     } else if (USER(i) == -2) {
550 	WLHELPER(0, col1, highlight, bg);
551     } else
552 	WLHELPER(0, col1, fg, bg);
553     /* This is the cursor, and it may need to be variable-width */
554     if ((CURROW >= 0) && (CURCOL >= FC) && (CURCOL < FC + COLS) &&
555 	(i == NUMLINES - 1) && (p->sel == 0 || !intersect_input(p, 0))) {
556 	if (!p->overwrite) {
557 	    if (p->cursor_blink) {
558 	    	setcaret(c, BORDERX + (CURCOL - FC) * FW, BORDERY + j * FH,
559 	    	            p->cursor_blink == 1 ? 1 : FW/4, FH);
560 	    	showcaret(c, 1);
561 	    } else showcaret(c, 0);
562 
563 	    if (p->cursor_blink < 2) {
564 	    	r = rect(BORDERX + (CURCOL - FC) * FW, BORDERY + j * FH, FW/4, FH);
565 	    	gfillrect(BM, highlight, r);
566 	    }
567 	} else if(mbcslocale) { /* determine the width of the current char */
568 	    int w0;
569 	    wchar_t *P = s, wc = 0, nn[2] = L" ";
570 	    for (w0 = 0; w0 <= CURCOL; P++) {
571 		wc = *P;
572 		if(!*P) break;
573 		w0 += Ri18n_wcwidth(wc);
574 	    }
575 	    /* term string '\0' box width = 1 fix */
576 	    w0 = wc ? Ri18n_wcwidth(wc) : 1;
577 	    nn[0] = wc;
578 	    if (p->cursor_blink) {
579 	    	setcaret(c, BORDERX + (CURCOL - FC) * FW, BORDERY + j * FH,
580 	    		    p->cursor_blink == 1 ? 1 : FW/4, FH);
581 	    	showcaret(c, 1);
582 	    } else showcaret(c, 0);
583 	    if (p->cursor_blink < 2) {
584 	    	r = rect(BORDERX + (CURCOL - FC) * FW, BORDERY + j * FH,
585 		         w0 * FW, FH);
586 	    	gfillrect(BM, highlight, r);
587 	    	gdrawwcs(BM, p->f, bg, pt(r.x, r.y), nn);
588 	    }
589 	} else {
590 	    if (p->cursor_blink) {
591 		setcaret(c, BORDERX + (CURCOL - FC) * FW, BORDERY + j * FH,
592 		            p->cursor_blink == 1 ? 1 : FW, FH);
593 	    	showcaret(c, 1);
594 	    } else showcaret(c, 0);
595 	    if (p->cursor_blink < 2)
596 	    	WLHELPER(CURCOL - FC, CURCOL - FC, bg, highlight);
597 	}
598     }
599     if (insel != 0) return len;
600     c1 = (p->my0 < p->my1);
601     c2 = (p->my0 == p->my1);
602     c3 = (p->mx0 < p->mx1);
603     if (c1 || (c2 && c3)) {
604 	x0 = p->mx0; y0 = p->my0;
605 	x1 = p->mx1; y1 = p->my1;
606     } else {
607 	x0 = p->mx1; y0 = p->my1;
608 	x1 = p->mx0; y1 = p->my0;
609     }
610     if (i == y0) {
611 	if (FC + COLS < x0) return len;
612 	if(mbcslocale) {
613 	    int w0, w1 = 1;
614 	    wchar_t *P = s;
615 	    for (w0 = 0; w0 < x0; P++) {
616 		if(!*P) break;
617 		w1 = Ri18n_wcwidth(*P);
618 		w0 += w1;
619 	    }
620 	    if(w0 > x0) x0 = w0 - w1;
621 	}
622 	c1 = (x0 > FC) ? (x0 - FC) : 0;
623     } else
624 	c1 = 0;
625     if (i == y1) {
626 	if (FC > x1) return len;
627 	if(mbcslocale) {
628 	    int w0;
629 	    wchar_t *P = s;
630 	    for (w0 = 0; w0 <= x1; P++) {
631 		if(!*P) break;
632 		w0 += Ri18n_wcwidth(*P);
633 	    }
634 	    x1 = w0 - 1;
635 	}
636 	c2 = (x1 > FC + COLS) ? (COLS - 1) : (x1 - FC);
637     } else
638 	c2 = COLS - 1;
639     WLHELPER(c1, c2, bg, fg);
640     return len;
641 }
642 
drawconsole(control c,rect r)643 void drawconsole(control c, rect r) /* r is unused here */
644 {
645     ConsoleData p = getdata(c);
646 
647     int i, ll, wd, maxwd = 0;
648 
649     ll = min(NUMLINES, ROWS);
650     if(!BM) return;;     /* This is a workaround for PR#1711.
651 			    BM should never be null here */
652     if (p->kind == PAGER)
653 	gfillrect(BM, p->guiColors[pagerbg], getrect(BM));
654     else
655 	gfillrect(BM, p->guiColors[consolebg], getrect(BM));
656     if(!ll) return;;
657     for (i = 0; i < ll; i++) {
658 	wd = WRITELINE(NEWFV + i, i);
659 	if(wd > maxwd) maxwd = wd;
660     }
661     RSHOW(getrect(c));
662     FV = NEWFV;
663     p->needredraw = 0;
664 /* always display scrollbar if FC > 0 */
665     if(maxwd < COLS - 1) maxwd = COLS - 1;
666     maxwd += FC;
667     gchangescrollbar(c, HWINSB, FC, maxwd-FC, COLS,
668 		     p->kind == CONSOLE || NUMLINES > ROWS);
669     gchangescrollbar(c, VWINSB, FV, NUMLINES - 1 , ROWS, p->kind == CONSOLE);
670 }
671 
setfirstvisible(control c,int fv)672 void setfirstvisible(control c, int fv)
673 {
674     ConsoleData p = getdata(c);
675 
676     int  ds, rw, ww;
677 
678     if (NUMLINES <= ROWS) return;;
679     if (fv < 0) fv = 0;
680     else if (fv > NUMLINES - ROWS) fv = NUMLINES - ROWS;
681     if (fv < 0) fv = 0;
682     ds = fv - FV;
683     if ((ds == 0) && !p->needredraw) return;;
684     if (abs(ds) > 1) {
685 	NEWFV = fv;
686 	REDRAW;
687 	return;;
688     }
689     if (p->needredraw) {
690 	ww = min(NUMLINES, ROWS) - 1;
691 	rw = FV + ww;
692 	writeline(c, p, rw, ww);
693 	if (ds == 0) {
694 	    RSHOW(RLINE(ww));
695 	    return;;
696 	}
697     }
698     if (ds == 1) {
699 	gscroll(BM, pt(0, -FH), RMLINES(0, ROWS - 1));
700 	if (p->kind == PAGER)
701 	    gfillrect(BM, p->guiColors[pagerbg], RLINE(ROWS - 1));
702 	else
703 	    gfillrect(BM, p->guiColors[consolebg], RLINE(ROWS - 1));
704 	WRITELINE(fv + ROWS - 1, ROWS - 1);
705     }
706     else if (ds == -1) {
707 	gscroll(BM, pt(0, FH), RMLINES(0, ROWS - 1));
708 	if (p->kind == PAGER)
709 	    gfillrect(BM, p->guiColors[pagerbg], RLINE(0));
710 	else
711 	    gfillrect(BM, p->guiColors[consolebg], RLINE(0));
712 	WRITELINE(fv, 0);
713     }
714     RSHOW(getrect(c));
715     FV = fv;
716     NEWFV = fv;
717     p->needredraw = 0;
718     gchangescrollbar(c, VWINSB, fv, NUMLINES - 1 , ROWS, p->kind == CONSOLE);
719 }
720 
setfirstcol(control c,int newcol)721 void setfirstcol(control c, int newcol)
722 {
723     ConsoleData p = getdata(c);
724 
725     int i, ml, li, ll;
726 
727     ll = (NUMLINES < ROWS) ? NUMLINES : ROWS;
728     if (newcol > 0) {
729 	for (i = 0, ml = 0; i < ll; i++) {
730 	    /* <FIXME> this should really take \r into account */
731 	    li = wcswidth(LINE(NEWFV + i));
732 	    ml = (ml < li) ? li : ml;
733 	}
734 	ml = ml - COLS;
735 	ml = 5*(ml/5 + 1);
736 	if (newcol > ml) newcol = ml;
737     }
738     if (newcol < 0) newcol = 0;
739     FC = newcol;
740     REDRAW;
741 }
742 
console_mousedrag(control c,int button,point pt)743 void console_mousedrag(control c, int button, point pt)
744 {
745     ConsoleData p = getdata(c);
746 
747     pt.x -= BORDERX;
748     pt.y -= BORDERY;
749     if (button & LeftButton) {
750 	int r, s;
751 	r=((pt.y > 32000) ? 0 : ((pt.y > HEIGHT) ? HEIGHT : pt.y))/FH;
752 	s=((pt.x > 32000) ? 0 : ((pt.x > WIDTH) ? WIDTH : pt.x))/FW;
753 	if ((r < 0) || (r > ROWS) || (s < 0) || (s > COLS))
754 	    return;;
755 	p->my1 = FV + r;
756 	p->mx1 = FC + s;
757 	p->needredraw = 1;
758 	p->sel = 1;
759 
760 	if (within_input(p, p->mx1, p->my1)) {
761 	    cur_pos = col_to_pos(p, p->mx1);
762 	    setCURCOL(p);
763 	}
764 	if (pt.y <= 0) setfirstvisible(c, FV - 3);
765 	else if (pt.y >= ROWS*FH) setfirstvisible(c, FV+3);
766 	if (pt.x <= 0) setfirstcol(c, FC - 3);
767 	else if (pt.x >= COLS*FW) setfirstcol(c, FC+3);
768 	else REDRAW;
769     }
770 }
771 
console_mouserep(control c,int button,point pt)772 void console_mouserep(control c, int button, point pt)
773 {
774     ConsoleData p = getdata(c);
775 
776     if ((button & LeftButton) && (p->sel)) console_mousedrag(c, button,pt);
777 }
778 
console_mousedown(control c,int button,point pt)779 void console_mousedown(control c, int button, point pt)
780 {
781     ConsoleData p = getdata(c);
782 
783     pt.x -= BORDERX;
784     pt.y -= BORDERY;
785     if (p->sel) {
786 	p->sel = 0;
787 	p->needredraw = 1;
788     }
789     if (button & LeftButton) {
790 	p->my0 = FV + pt.y/FH;
791 	p->mx0 = FC + pt.x/FW;
792 	if (within_input(p, p->mx0, p->my0) ||
793 	    (p->my0 == CURROW && p->mx0 > prompt_wid)) {
794 	    cur_pos = col_to_pos(p, p->mx0);
795 	    setCURCOL(p);
796 	    p->needredraw = 1;
797 	}
798     }
799     if (p->needredraw) REDRAW;
800 }
801 
consoletogglelazy(control c)802 void consoletogglelazy(control c)
803 {
804     ConsoleData p = getdata(c);
805 
806     if (p->kind == PAGER) return;
807     p->lazyupdate = (p->lazyupdate + 1) % 2;
808 }
809 
consolegetlazy(control c)810 int consolegetlazy(control c)
811 {
812     ConsoleData p = getdata(c);
813     return p->lazyupdate;
814 }
815 
816 
consoleflush(control c)817 void consoleflush(control c)
818 {
819     REDRAW;
820 }
821 
822 
823 /* These are the getline keys ^A ^E ^B ^F ^N ^P ^K ^H ^D ^U ^T ^O,
824    plus ^Z for EOF.
825 
826    We also use ^C ^V/^Y ^X (copy/paste/both) ^W ^L
827 */
828 #define BEGINLINE 1
829 #define ENDLINE   5
830 #define CHARLEFT 2
831 #define CHARRIGHT 6
832 #define NEXTHISTORY 14
833 #define PREVHISTORY 16
834 #define KILLRESTOFLINE 11
835 #define BACKCHAR  8
836 #define DELETECHAR 4
837 #define KILLLINE 21
838 #define CHARTRANS 20
839 #define OVERWRITE 15
840 #define EOFKEY 26
841 
842 /* ^I for completion */
843 
844 #define TABKEY 9
845 
846 /* free ^G ^Q ^R ^S, perhaps ^J */
847 
checkpointpos(xbuf p,int save)848 static void checkpointpos(xbuf p, int save)
849 {
850     static int ns, av;
851     static wchar_t *free;
852     if(save) {
853 	ns = p->ns;
854 	av = p->av;
855 	free = p->free;
856     } else {
857 	p->ns = ns;
858 	p->av = av;
859 	p->free = free;
860     }
861 }
862 
storekey(control c,int k)863 static void storekey(control c, int k)
864 {
865     ConsoleData p = getdata(c);
866 
867     if (p->wipe_completion) {
868 	p->wipe_completion = 0;
869 	checkpointpos(p->lbuf, 0);
870 	/* mark whole of current line as user input */
871 	USER(NUMLINES-1) = 0;
872 	p->needredraw = 1;
873 	REDRAW;
874     }
875     if (p->kind == PAGER) return;
876     if (k == BKSP) k = BACKCHAR;
877     if (k == TABKEY) {
878 	performCompletion(c);
879 	return;
880     }
881     if (p->numkeys >= NKEYS) {
882 	gabeep();
883 	return;;
884     }
885     p->kbuf[(p->firstkey + p->numkeys) % NKEYS] = k;
886     p->numkeys++;
887 }
888 
storetab(control c)889 static void storetab(control c)
890 {
891     ConsoleData p = getdata(c);
892     p->kbuf[(p->firstkey + p->numkeys) % NKEYS] = L' ';
893     p->numkeys++;
894 }
895 
896 
897 #include <Rinternals.h>
898 #include <R_ext/Parse.h>
899 
900 static int completion_available = -1;
901 
set_completion_available(int x)902 void set_completion_available(int x)
903 {
904     completion_available = x;
905 }
906 
907 
performCompletion(control c)908 static void performCompletion(control c)
909 {
910     ConsoleData p = getdata(c);
911     int i, alen, alen2, max_show = 10, cursor_position = CURCOL - prompt_wid;
912     wchar_t *partial_line = LINE(NUMLINES - 1) + prompt_wid;
913     const char *additional_text;
914     SEXP cmdSexp, cmdexpr, ans = R_NilValue;
915     ParseStatus status;
916 
917     if(!completion_available) {
918 	storetab(c);
919 	return;
920     }
921 
922     if(completion_available < 0) {
923 	char *p = getenv("R_COMPLETION");
924 	if(p && strcmp(p, "FALSE") == 0) {
925 	    completion_available = 0;
926 	    storetab(c);
927 	    return;
928 	}
929 	/* First check if namespace is loaded */
930 	if(findVarInFrame(R_NamespaceRegistry, install("utils"))
931 	   != R_UnboundValue) completion_available = 1;
932 	else { /* Then try to load it */
933 	    char *p = "try(loadNamespace('utils'), silent=TRUE)";
934 	    PROTECT(cmdSexp = mkString(p));
935 	    cmdexpr = PROTECT(R_ParseVector(cmdSexp, -1, &status, R_NilValue));
936 	    if(status == PARSE_OK) {
937 		for(i = 0; i < length(cmdexpr); i++)
938 		    eval(VECTOR_ELT(cmdexpr, i), R_GlobalEnv);
939 	    }
940 	    UNPROTECT(2);
941 	    if(findVarInFrame(R_NamespaceRegistry, install("utils"))
942 	       != R_UnboundValue) completion_available = 1;
943 	    else {
944 		completion_available = 0;
945 		return;
946 	    }
947 	}
948     }
949 
950     alen = wcslen(partial_line);
951     wchar_t orig[alen + 1], pline[2*alen + 1],
952             *pchar = pline, achar;
953     wcscpy(orig, partial_line);
954     for (i = 0; i < alen; i++) {
955         achar = orig[i];
956 	if (achar == '"' || achar == '\\') *pchar++ = '\\';
957 	*pchar++ = achar;
958     }
959     *pchar = 0;
960     size_t len = wcslen(pline) + 100;
961     char cmd[len];
962     snprintf(cmd, len, "utils:::.win32consoleCompletion(\"%ls\", %d)",
963 	     pline, cursor_position);
964     PROTECT(cmdSexp = mkString(cmd));
965     cmdexpr = PROTECT(R_ParseVector(cmdSexp, -1, &status, R_NilValue));
966     if (status != PARSE_OK) {
967 	UNPROTECT(2);
968 	/* Uncomment next line to debug */
969 	/* Rprintf("failed: %s \n", cmd); */
970 	/* otherwise pretend that nothing happened and return */
971 	return;
972     }
973     /* Loop is needed here as EXPSEXP will be of length > 1 */
974     for(i = 0; i < length(cmdexpr); i++)
975 	ans = eval(VECTOR_ELT(cmdexpr, i), R_GlobalEnv);
976     UNPROTECT(2);
977 
978     /* ans has the form list(addition, possible), where 'addition' is
979        unique additional text if any, and 'possible' is a character
980        vector holding possible completions if any (already formatted
981        for linewise printing in the current implementation).  If
982        'possible' has any content, we want to print those (or show in
983        status bar or whatever).  Otherwise add the 'additional' text
984        at the cursor */
985 
986 #define ADDITION 0
987 #define POSSIBLE 1
988 
989     alen = length(VECTOR_ELT(ans, POSSIBLE));
990     additional_text = CHAR(STRING_ELT( VECTOR_ELT(ans, ADDITION), 0 ));
991     alen2 = strlen(additional_text);
992     if (alen) {
993 	/* make a copy of the current string first */
994 	wchar_t p1[wcslen(LINE(NUMLINES - 1)) + 1];
995 	wcscpy(p1, LINE(NUMLINES - 1));
996 	checkpointpos(p->lbuf, 1);
997 	size_t len = MB_CUR_MAX * wcslen(p1) + 1;
998 	char buf1[len+1];
999 	snprintf(buf1, len+1, "%ls\n", p1);
1000 	consolewrites(c, buf1);
1001 
1002 	for (i = 0; i < min(alen, max_show); i++) {
1003             consolewrites(c, "\n");
1004 	    consolewrites(c, CHAR(STRING_ELT(VECTOR_ELT(ans, POSSIBLE), i)));
1005 	}
1006 	if (alen > max_show)
1007 	    consolewrites(c, "\n[...truncated]");
1008 	consolewrites(c, "\n");
1009 	p->wipe_completion = 1;
1010     }
1011 
1012     if (alen2)
1013 	for (i = 0; i < alen2; i++) storekey(c, additional_text[i]);
1014     return;
1015 }
1016 
1017 /* deletes that part of the selection which is on the input line */
deleteselected(ConsoleData p)1018 static void deleteselected(ConsoleData p)
1019 {
1020     if (p->sel) {
1021 	int s0, s1;
1022 	wchar_t *cur_line;
1023 	if (intersect_input(p, 1)) {
1024 	    /* convert to bytes after the prompt */
1025 	    s0 = col_to_pos(p, p->mx0);
1026 	    s1 = col_to_pos(p, p->mx1);
1027 	    cur_line = LINE(CURROW) + prompt_len;
1028 	    for(int i = s0; i <= max_pos; i++)
1029 		cur_line[i] = cur_line[i + s1 - s0 + 1];
1030 	    max_pos -= s1 - s0 + 1;
1031 	    cur_line[max_pos] = L'\0';
1032 	    if (cur_pos > s0)
1033 		cur_pos = cur_pos > s1 ? cur_pos - (s1 - s0 + 1) : s0;
1034 	    setCURCOL(p);
1035 	    p->needredraw = 1;
1036 	}
1037     }
1038 }
1039 
1040 /* cmd is in native encoding */
consolecmd(control c,const char * cmd)1041 void consolecmd(control c, const char *cmd)
1042 {
1043     ConsoleData p = getdata(c);
1044 
1045     int i;
1046     if (p->sel) {
1047 	deleteselected(p);
1048 	p->sel = 0;
1049 	p->needredraw = 1;
1050 	REDRAW;
1051     }
1052     storekey(c, BEGINLINE);
1053     storekey(c, KILLRESTOFLINE);
1054     if(isUnicodeWindow(c)) {
1055 	size_t sz = (strlen(cmd) + 1) * sizeof(wchar_t);
1056 	wchar_t *wcs = (wchar_t*) R_alloc(strlen(cmd) + 1, sizeof(wchar_t));
1057 	memset(wcs, 0, sz);
1058 	mbstowcs(wcs, cmd, sz-1);
1059 	for(i = 0; wcs[i]; i++) storekey(c, wcs[i]);
1060     } else {
1061 	const char *ch;
1062 	for (ch = cmd; *ch; ch++) storekey(c, (unsigned char) *ch);
1063     }
1064     storekey(c, '\n');
1065 /* if we are editing we save the actual line
1066    FIXME: not right if Unicode */
1067     if (CURROW > -1) {
1068 	char buf[2000], *cp; /* maximum 2 bytes/char */
1069 	wchar_t *wc = &(LINE(NUMLINES - 1)[prompt_len]);
1070 	memset(buf, 0, 2000);
1071 	wcstombs(buf, wc, 1000);
1072 	for (cp = buf; *cp; cp++) storekey(c, *cp);
1073 	for (i = max_pos; i > cur_pos; i--) storekey(c, CHARLEFT);
1074     }
1075 }
1076 
CleanTranscript(wchar_t * tscpt,wchar_t * cmds)1077 static int CleanTranscript(wchar_t *tscpt, wchar_t *cmds)
1078 {
1079     /*
1080      * Filter R commands out of a string that contains
1081      * prompts, commands, and output.
1082      * Uses a simple algorithm that just looks for '>'
1083      * prompts and '+' continuation following a simple prompt prefix.
1084      * Always return the length of the string required
1085      * to hold the filtered commands.
1086      * If cmds is a non-null pointer, write the commands
1087      * to cmds & terminate with null.
1088      */
1089     int incommand = 0, startofline = 1, len = 0;
1090     wchar_t nonprefix[] = L">+ \t\n\r";
1091     while (*tscpt) {
1092 	if (startofline) {
1093 	    /* skip initial whitespace */
1094 	    while (*tscpt == L' ' || *tscpt == L'\t') tscpt++;
1095 	    /* skip over the prompt prefix */
1096 	    while (*tscpt && !wcschr(nonprefix, *tscpt)) tscpt++;
1097 	    if (*tscpt == L'>' || (incommand && *tscpt == L'+')) {
1098 		tscpt++;
1099 		if (*tscpt == L' ' || *tscpt == L'\t') tscpt++;
1100 		incommand = 1;
1101 	    } else {
1102 		incommand = 0;
1103 	    }
1104 	    startofline = 0;
1105 	} else {
1106 	    if (incommand) {
1107 		if (cmds) *(cmds++) = *tscpt;
1108 		len++;
1109 	    }
1110 	    if (*tscpt == L'\n') startofline = 1;
1111 	    tscpt++;
1112 	}
1113     }
1114     if (cmds) {
1115 	/* seem to have to terminate with two nulls, otherwise
1116 	   pasting empty commands doesn't work correctly (e.g.,
1117 	   when clipboard contains 'XXX') */
1118 	*cmds = '\0';
1119 	*(cmds+1) = '\0';
1120     }
1121     return(len+2);
1122 }
1123 
1124 /* Send a single newline to the console */
consolenewline(control c)1125 void consolenewline(control c)
1126 {
1127     storekey(c, '\n');
1128 }
1129 
1130 /* the following four routines are system dependent */
consolepaste(control c)1131 void consolepaste(control c)
1132 {
1133     ConsoleData p = getdata(c);
1134 
1135     HGLOBAL hglb;
1136     wchar_t *pc, *new = NULL;
1137     if (p->sel) {
1138 	deleteselected(p);
1139 	p->sel = 0;
1140 	p->needredraw = 1;
1141 	REDRAW;
1142      }
1143     if (p->kind == PAGER) return;
1144     if ( OpenClipboard(NULL) &&
1145 	 (hglb = GetClipboardData(CF_UNICODETEXT)) &&
1146 	 (pc = (wchar_t *) GlobalLock(hglb)))
1147     {
1148 	if (p->clp) {
1149 	   new = realloc((void *)p->clp,
1150 			 (wcslen(p->clp) + wcslen(pc) + 1) * sizeof(wchar_t));
1151 	}
1152 	else {
1153 	   new = malloc((wcslen(pc) + 1) * sizeof(wchar_t)) ;
1154 	   if (new) new[0] = L'\0';
1155 	   p->already = p->numkeys;
1156 	   p->pclp = 0;
1157 	}
1158 	if (new) {
1159 	   int i;
1160 	   p->clp = new;
1161 	   /* Surrogate Pairs Block */
1162 	   for (i = 0; i < wcslen(pc); i++)
1163 	       if (IsSurrogatePairsHi(pc[i]) && i+1 < wcslen(pc) &&
1164 		    IsSurrogatePairsLo(pc[i+1]) ) {
1165 		   pc[i] = L'?';
1166 		   pc[i+1] = L'?';
1167 		   i++;
1168 	       }
1169 	   wcscat(p->clp, pc);
1170 	}
1171 	else {
1172 	   R_ShowMessage(G_("Not enough memory"));
1173 	}
1174 	GlobalUnlock(hglb);
1175     }
1176     CloseClipboard();
1177 }
1178 
consolepastecmds(control c)1179 void consolepastecmds(control c)
1180 {
1181     ConsoleData p = getdata(c);
1182 
1183     HGLOBAL hglb;
1184     wchar_t *pc, *new = NULL;
1185     if (p->sel) {
1186 	deleteselected(p);
1187 	p->sel = 0;
1188 	p->needredraw = 1;
1189 	REDRAW;
1190      }
1191     if (p->kind == PAGER) return;;
1192     if ( OpenClipboard(NULL) &&
1193 	 (hglb = GetClipboardData(CF_UNICODETEXT)) &&
1194 	 (pc = (wchar_t *) GlobalLock(hglb)))
1195     {
1196 	if (p->clp) {
1197 	    new = realloc((void *)p->clp,
1198 			  (wcslen(p->clp) + CleanTranscript(pc, 0))
1199 			  * sizeof(wchar_t));
1200 	}
1201 	else {
1202 	    new = malloc(CleanTranscript(pc, 0) * sizeof(wchar_t));
1203 	    if (new) new[0] = '\0';
1204 	    p->already = p->numkeys;
1205 	    p->pclp = 0;
1206 	}
1207 	if (new) {
1208 	    p->clp = new;
1209 	    /* copy just the commands from the clipboard */
1210 	    for (; *new; ++new); /* append to the end of 'new' */
1211 	    CleanTranscript(pc, new);
1212 	}
1213 	else {
1214 	    R_ShowMessage(G_("Not enough memory"));
1215 	}
1216 	GlobalUnlock(hglb);
1217     }
1218     CloseClipboard();
1219 }
1220 
1221 /* This works with columns, not chars or bytes */
consoletoclipboardHelper(control c,int x0,int y0,int x1,int y1)1222 static void consoletoclipboardHelper(control c, int x0, int y0, int x1, int y1)
1223 {
1224     ConsoleData p = getdata(c);
1225 
1226     HGLOBAL hglb;
1227     int ll, i, j;
1228     wchar_t *s;
1229 
1230     if(mbcslocale) {
1231 	int w0, x00 = x0, x11=100000;
1232 	i = y0; ll = 1; /* terminator */
1233 	while (i <= y1) {
1234 	    wchar_t *P = LINE(i);
1235 	    for (w0 = 0; w0 < x00 && *P; P++) w0 += Ri18n_wcwidth(*P);
1236 	    x00 = 0;
1237 	    if(i == y1) x11 = x1+1; /* cols are 0-based */
1238 	    while (w0 < x11 && *P) {
1239 		ll++;
1240 		w0 += Ri18n_wcwidth(*P++);
1241 	    }
1242 	    if(w0 < x11) ll += 2;  /* \r\n */
1243 	    i++;
1244 	}
1245     } else {
1246 	i = y0; j = x0; ll = 1; /* terminator */
1247 	while ((i < y1) || ((i == y1) && (j <= x1))) {
1248 	    if (LINE(i)[j]) {
1249 		ll++;
1250 		j++;
1251 	    } else {
1252 		ll += 2;
1253 		i++;
1254 		j = 0;
1255 	    }
1256 	}
1257     }
1258 
1259 
1260     if (!(hglb = GlobalAlloc(GHND, ll * sizeof(wchar_t)))){
1261 	R_ShowMessage(G_("Insufficient memory: text not copied to the clipboard"));
1262 	return;
1263     }
1264     if (!(s = (wchar_t *)GlobalLock(hglb))){
1265 	R_ShowMessage(G_("Insufficient memory: text not copied to the clipboard"));
1266 	return;
1267     }
1268     if(mbcslocale) {
1269 	int w0, x00 = x0, x11=100000;
1270 	wchar_t *P;
1271 	i = y0;
1272 	while (i <= y1) {
1273 	    P = LINE(i);
1274 	    for (w0 = 0; w0 < x00 && *P; P++) w0 += Ri18n_wcwidth(*P);
1275 	    x00 = 0;
1276 	    if(i == y1) x11 = x1+1;
1277 	    while (w0 < x11 && *P) {
1278 		w0 += Ri18n_wcwidth(*P);
1279 		*s++ = *P++;
1280 	    }
1281 	    if(w0 < x11) {
1282 		*s++ = L'\r'; *s++ = L'\n';
1283 	    }
1284 	    i++;
1285 	}
1286     } else {
1287 	i = y0; j = x0;
1288 	while ((i < y1) || ((i == y1) && (j <= x1))) {
1289 	    wchar_t ch = LINE(i)[j];
1290 	    if (ch) {
1291 		*s++ = ch;
1292 		j++;
1293 	    } else {
1294 		*s++ = L'\r'; *s++ = L'\n';
1295 		i++;
1296 		j = 0;
1297 	    }
1298 	}
1299     }
1300     *s = L'\0';
1301     GlobalUnlock(hglb);
1302     if (!OpenClipboard(NULL) || !EmptyClipboard()) {
1303 	R_ShowMessage(G_("Unable to open the clipboard"));
1304 	GlobalFree(hglb);
1305 	return;;
1306     }
1307     SetClipboardData(CF_UNICODETEXT, hglb);
1308     CloseClipboard();
1309 }
1310 
1311 /* end of system dependent part */
1312 
consolecanpaste(control c)1313 int consolecanpaste(control c)
1314 {
1315     return clipboardhastext();
1316 }
1317 
1318 
consolecancopy(control c)1319 int consolecancopy(control c)
1320 {
1321     ConsoleData p = getdata(c);
1322     return p->sel;
1323 }
1324 
1325 
consolecopy(control c)1326 void consolecopy(control c)
1327 {
1328     ConsoleData p = getdata(c);
1329 
1330     if (p->sel) {
1331 	int len, c1, c2, c3;
1332 	int x0, y0, x1, y1;
1333 	if (p->my0 >= NUMLINES) p->my0 = NUMLINES - 1;
1334 	if (p->my0 < 0) p->my0 = 0;
1335 	len = wcswidth(LINE(p->my0));
1336 	if (p->mx0 >= len) p->mx0 = len - 1;
1337 	if (p->mx0 < 0) p->mx0 = 0;
1338 	if (p->my1 >= NUMLINES) p->my1 = NUMLINES - 1;
1339 	if (p->my1 < 0) p->my1 = 0;
1340 	len = wcswidth(LINE(p->my1));
1341 	if (p->mx1 >= len) p->mx1 = len/* - 1*/;
1342 	if (p->mx1 < 0) p->mx1 = 0;
1343 	c1 = (p->my0 < p->my1);
1344 	c2 = (p->my0 == p->my1);
1345 	c3 = (p->mx0 < p->mx1);
1346 	if (c1 || (c2 && c3)) {
1347 	   x0 = p->mx0; y0 = p->my0;
1348 	   x1 = p->mx1; y1 = p->my1;
1349 	}
1350 	else {
1351 	   x0 = p->mx1; y0 = p->my1;
1352 	   x1 = p->mx0; y1 = p->my0;
1353 	}
1354 	consoletoclipboardHelper(c, x0, y0, x1, y1);
1355 	REDRAW;
1356     }
1357 }
1358 
consoleselectall(control c)1359 void consoleselectall(control c)
1360 {
1361     ConsoleData p = getdata(c);
1362 
1363    if (NUMLINES) {
1364        p->sel = 1;
1365        p->my0 = p->mx0 = 0;
1366        p->my1 = NUMLINES - 1;
1367        p->mx1 = wcslen(LINE(p->my1));
1368        REDRAW;
1369     }
1370 }
1371 
1372 /*
1373    This works in CJK as the IME puts CJK characters in the
1374    input buffer as 2 bytes, and they are retrieved successively
1375 */
console_normalkeyin(control c,int k)1376 void console_normalkeyin(control c, int k)
1377 {
1378     ConsoleData p = getdata(c);
1379 
1380     int st;
1381 
1382     st = ggetkeystate();
1383     if ((p->chbrk) && (k == p->chbrk) &&
1384 	((!p->modbrk) || ((p->modbrk) && (st == p->modbrk)))) {
1385 	p->fbrk(c);
1386 	return;
1387     }
1388     if (st == CtrlKey)
1389 	switch (k + 'A' - 1) {
1390 	    /* most are stored as themselves */
1391 	case 'C':
1392 	    consolecopy(c);
1393 	    st = -1;
1394 	    break;
1395 	case 'V':
1396 	case 'Y':
1397 	    if(p->kind == PAGER) {
1398 		consolecopy(c);
1399 		if (CharacterMode == RGui) consolepaste(RConsole);
1400 	    }
1401 	    else consolepaste(c);
1402 	    st = -1;
1403 	    break;
1404 	case 'X':
1405 	    consolecopy(c);
1406 	    consolepaste(c);
1407 	    st = -1;
1408 	    break;
1409 	case 'W':
1410 	    consoletogglelazy(c);
1411 	    st = -1;
1412 	    break;
1413 	case 'L':
1414 	    consoleclear(c);
1415 	    st = -1;
1416 	    break;
1417 	case 'O':
1418 	    p->overwrite = !p->overwrite;
1419 	    p->needredraw = 1;
1420 	    st = -1;
1421 	    break;
1422 	}
1423     if (p->sel) {
1424 	if (st != -1) deleteselected(p);
1425 	p->needredraw = 1;
1426 	p->sel = 0;
1427     }
1428     if (p->needredraw) REDRAW;
1429     if (st == -1) return;
1430     if (p->kind == PAGER) {
1431 	if(k == 'q' || k == 'Q') pagerbclose(c);
1432 	if(k == ' ') setfirstvisible(c, NEWFV + ROWS);
1433 	if(k == '-') setfirstvisible(c, NEWFV - ROWS);
1434 	if(k == 'F' - 'A' + 1) setfirstvisible(c, NEWFV + ROWS);
1435 	if(k == 'B' - 'A' + 1) setfirstvisible(c, NEWFV - ROWS);
1436 	if(k == 1) consoleselectall(c);
1437 	return;
1438     }
1439     storekey(c, k);
1440 }
1441 
console_ctrlkeyin(control c,int key)1442 void console_ctrlkeyin(control c, int key)
1443 {
1444     ConsoleData p = getdata(c);
1445 
1446     int st;
1447 
1448     st = ggetkeystate();
1449     if ((p->chbrk) && (key == p->chbrk) &&
1450 	((!p->modbrk) || ((p->modbrk) && (st == p->modbrk)))) {
1451 	p->fbrk(c);
1452 	return;
1453     }
1454     switch (key) {
1455      case PGUP: setfirstvisible(c, NEWFV - ROWS); break;
1456      case PGDN: setfirstvisible(c, NEWFV + ROWS); break;
1457      case HOME:
1458 	 if (st == CtrlKey)
1459 	     setfirstvisible(c, 0);
1460 	 else
1461 	     if (p->kind == PAGER)
1462 		 setfirstcol(c, 0);
1463 	     else
1464 		 storekey(c, BEGINLINE);
1465 	 break;
1466      case END:
1467 	 if (st == CtrlKey)
1468 	     setfirstvisible(c, NUMLINES);
1469 	 else
1470 	     storekey(c, ENDLINE);
1471 	 break;
1472      case UP:
1473 	 if ((st == CtrlKey) || (p->kind == PAGER))
1474 	     setfirstvisible(c, NEWFV - 1);
1475 	 else
1476 	     storekey(c, PREVHISTORY);
1477 	 break;
1478      case DOWN:
1479 	 if ((st == CtrlKey) || (p->kind == PAGER))
1480 	     setfirstvisible(c, NEWFV + 1);
1481 	 else
1482 	     storekey(c, NEXTHISTORY);
1483 	 break;
1484      case LEFT:
1485 	 if ((st == CtrlKey) || (p->kind == PAGER))
1486 	     setfirstcol(c, FC - 5);
1487 	 else
1488 	     storekey(c, CHARLEFT);
1489 	 break;
1490      case RIGHT:
1491 	 if ((st == CtrlKey) || (p->kind == PAGER))
1492 	     setfirstcol(c, FC + 5);
1493 	 else
1494 	     storekey(c, CHARRIGHT);
1495 	 break;
1496      case DEL:
1497 	 if (p->sel) {
1498 	     if (st == ShiftKey) consolecopy(c);
1499 	     deleteselected(p);
1500 	     p->sel = 0;
1501 	 } else  if (st == CtrlKey)
1502 	     storekey(c, KILLRESTOFLINE);
1503 	 else
1504 	     storekey(c, DELETECHAR);
1505 	 break;
1506      case ENTER:
1507 	 deleteselected(p);
1508 	 storekey(c, '\n');
1509 	 break;
1510      case INS:
1511 	 if (st == ShiftKey) {
1512 	     deleteselected(p);
1513 	     consolepaste(c);
1514 	 } else {
1515 	     p->overwrite = !p->overwrite;
1516 	     p->needredraw = 1;
1517 	 }
1518 	 break;
1519     }
1520     if (p->sel) {
1521 	p->sel = 0;
1522 	p->needredraw = 1;
1523     }
1524     if (p->needredraw) REDRAW;
1525 }
1526 
1527 static Rboolean incomplete = FALSE;
consolewrites(control c,const char * s)1528 int consolewrites(control c, const char *s)
1529 {
1530     ConsoleData p = getdata(c);
1531 
1532     wchar_t buf[1001];
1533     if(p->input) {
1534 	int i, len = wcslen(LINE(NUMLINES - 1));
1535 	/* save the input line */
1536 	wcsncpy(buf, LINE(NUMLINES - 1), 1000);
1537 	buf[1000] = L'\0';
1538 	/* now zap it */
1539 	for(i = 0; i < len; i++) xbufaddxc(p->lbuf, L'\b');
1540 	if (incomplete) {
1541 	    NUMLINES--;
1542 	    p->lbuf->free--;
1543 	    p->lbuf->av++;
1544 	}
1545 	USER(NUMLINES - 1) = -1;
1546     }
1547     xbufadds(p->lbuf, s, 0);
1548     FC = 0;
1549     if(p->input) {
1550 	incomplete = (s[strlen(s) - 1] != '\n');
1551 	if (incomplete) xbufaddxc(p->lbuf, L'\n');
1552 	xbufaddxs(p->lbuf, buf, 1);
1553     }
1554     if (strchr(s, '\n')) p->needredraw = 1;
1555     if (!p->lazyupdate) {
1556 	setfirstvisible(c, NUMLINES - ROWS);
1557 	REDRAW;
1558     } else if (CURROW >= 0)
1559 	setfirstvisible(c, NUMLINES - ROWS);
1560     else {
1561 	NEWFV = NUMLINES - ROWS;
1562 	if (NEWFV < 0) NEWFV = 0;
1563     }
1564     if(p->input) REDRAW;
1565     return 0;
1566 }
1567 
freeConsoleData(ConsoleData p)1568 void freeConsoleData(ConsoleData p)
1569 {
1570     if (!p) return;
1571     if (BM) del(BM);
1572     if (p->kind == CONSOLE) {
1573 	if (p->lbuf) xbufdel(p->lbuf);
1574 	if (p->kbuf) free(p->kbuf);
1575     }
1576     free(p);
1577 }
1578 
delconsole(control c)1579 static void delconsole(control c)
1580 {
1581     freeConsoleData(getdata(c));
1582 }
1583 
1584 /* console readline (coded looking to the GNUPLOT 3.5 readline)*/
consolegetc(control c)1585 static wchar_t consolegetc(control c)
1586 {
1587     ConsoleData p;
1588     wchar_t ch;
1589 
1590     p = getdata(c);
1591     while((p->numkeys == 0) && (!p->clp))
1592     {
1593 	R_WaitEvent();
1594 	R_ProcessEvents();
1595     }
1596     if (p->sel) {
1597 	deleteselected(p);
1598 	p->sel = 0;
1599 	p->needredraw = 1;
1600 	setCURCOL(p); /* Needed? */
1601 	REDRAW;
1602     }
1603     if (!p->already && p->clp) {
1604 	ch = p->clp[p->pclp++];
1605 	if (!(p->clp[p->pclp])) {
1606 	    free(p->clp);
1607 	    p->clp = NULL;
1608 	}
1609     } else {
1610 	if(isUnicodeWindow(c)) {
1611 	    ch = p->kbuf[p->firstkey];
1612 	    p->firstkey = (p->firstkey + 1) % NKEYS;
1613 	    p->numkeys--;
1614 	    if (p->already) p->already--;
1615 	} else {
1616 	    if(mbcslocale) {
1617 		/* Possibly multiple 'keys' for a single keystroke */
1618 		char tmp[20];
1619 		unsigned int used, i;
1620 
1621 		for(i = 0; i < MB_CUR_MAX; i++)
1622 		    tmp[i] = p->kbuf[(p->firstkey + i) % NKEYS];
1623 		used = mbrtowc(&ch, tmp, MB_CUR_MAX, NULL);
1624 		p->firstkey = (p->firstkey + used) % NKEYS;
1625 		p->numkeys -= used;
1626 		if (p->already) p->already -= used;
1627 	    } else {
1628 		ch = (unsigned char) p->kbuf[p->firstkey];
1629 		if(ch >=128) {
1630 		    char tmp[2] = " ";
1631 		    tmp[0] = ch;
1632 		    mbrtowc(&ch, tmp, 2, NULL);
1633 		}
1634 		p->firstkey = (p->firstkey + 1) % NKEYS;
1635 		p->numkeys--;
1636 		if (p->already) p->already--;
1637 	    }
1638 	}
1639     }
1640     return ch;
1641 }
1642 
consoleunputc(control c)1643 static void consoleunputc(control c)
1644 {
1645     ConsoleData p = getdata(c);
1646 
1647     if(p->clp) p->pclp--;
1648     else {
1649 	p->numkeys += 1;
1650 	if (p->firstkey > 0) p->firstkey -= 1;
1651 	else p->firstkey = NKEYS - 1;
1652     }
1653 }
1654 
1655 /* This scrolls as far left as possible */
checkvisible(control c)1656 static void checkvisible(control c)
1657 {
1658     ConsoleData p = getdata(c);
1659 
1660     int newfc;
1661 
1662     setfirstvisible(c, NUMLINES-ROWS);
1663     newfc = 0;
1664     while ((CURCOL <= newfc) || (CURCOL > newfc+COLS-2)) newfc += 5;
1665     if (newfc != FC) setfirstcol(c, newfc);
1666 }
1667 
draweditline(control c)1668 static void draweditline(control c)
1669 {
1670     ConsoleData p = getdata(c);
1671     setCURCOL(p);
1672     checkvisible(c);
1673     if (p->needredraw) {
1674 	REDRAW;
1675     } else {
1676 	WRITELINE(NUMLINES - 1, CURROW);
1677 	RSHOW(RLINE(CURROW));
1678     }
1679 }
1680 
1681 /* This needs to convert the nul-terminated wchar_t string 'in' to a
1682    sensible strinf in buf[len].  It must not be empty, as R will
1683    interpret that as EOF, and it should end in \n, as 'in' should do.
1684 
1685    Our strategy is to convert character by character to the current
1686    Windows locale, using \uxxxx escapes for invalid characters.
1687 */
1688 
wcstobuf(char * buf,int len,const wchar_t * in)1689 static void wcstobuf(char *buf, int len, const wchar_t *in)
1690 {
1691     int used, tot = 0;
1692     char *p = buf, tmp[7];
1693     const wchar_t *wc = in;
1694     wchar_t wc_check;
1695     mbstate_t mb_st;
1696 
1697     for(; wc; wc++, p+=used, tot+=used) {
1698 	if(tot >= len - 2) break;
1699 	used = wctomb(p, *wc);
1700 	if (used >= 0) {
1701 	    /* conversion was successful, but check that converting back gets
1702 	       the original result (it does not with best-fit transliteration)
1703 	       NOTE: WideCharToMultiByte may be faster */
1704 	    memset(&mb_st, 0, sizeof(mbstate_t));
1705 	    if (mbrtowc(&wc_check, p, used, &mb_st) < 0 || wc_check != *wc)
1706 		used = -1;
1707 	}
1708 	if (used < 0) {
1709 	    snprintf(tmp, 7, "\\u%x", *wc);
1710 	    used = strlen(tmp);
1711 	    memcpy(p, tmp, used);
1712 	}
1713     }
1714     *p++ = '\n'; *p = '\0';
1715 }
1716 
consolereads(control c,const char * prompt,char * buf,int len,int addtohistory)1717 int consolereads(control c, const char *prompt, char *buf, int len,
1718 		 int addtohistory)
1719 {
1720     ConsoleData p = getdata(c);
1721 
1722     wchar_t *cur_line, *P;
1723     wchar_t *aLine;
1724     int ns0 = NUMLINES, w0 = 0, pre_prompt_len;
1725 
1726     pre_prompt_len = wcslen(LINE(NUMLINES - 1));
1727     /* print the prompt */
1728     xbufadds(p->lbuf, prompt, 1);
1729     if (!xbufmakeroom(p->lbuf, len + 1)) return 1;
1730     P = aLine = LINE(NUMLINES - 1);
1731     prompt_len = wcslen(aLine);
1732     for (; P < aLine + pre_prompt_len; P++)
1733 	if(*P == L'\r') w0 = 0;
1734 	else w0 += mbcslocale ? Ri18n_wcwidth(*P) : 1;
1735     USER(NUMLINES - 1) = w0;
1736     prompt_wid = wcswidth(aLine);
1737     if (NUMLINES > ROWS) {
1738 	CURROW = ROWS - 1;
1739 	NEWFV = NUMLINES - ROWS;
1740     } else {
1741 	CURROW = NUMLINES - 1;
1742 	NEWFV = 0;
1743     }
1744     CURCOL = prompt_wid;
1745     FC = 0;
1746     cur_pos = 0;
1747     max_pos = 0;
1748     cur_line = &aLine[prompt_len];
1749     cur_line[0] = L'\0';
1750     showcaret(c, 1);
1751     REDRAW;
1752     for(;;) {
1753 	wchar_t cur_char;
1754 	char chtype; /* boolean */
1755 	p->input = 1;
1756 	cur_char = consolegetc(c);
1757 	p->input = 0;
1758 	chtype = ((unsigned int) cur_char > 0x1f);
1759 	if(NUMLINES != ns0) { /* we scrolled, e.g. cleared screen */
1760 	    cur_line = LINE(NUMLINES - 1) + prompt_len;
1761 	    ns0 = NUMLINES;
1762 	    if (NUMLINES > ROWS) {
1763 		CURROW = ROWS - 1;
1764 		NEWFV = NUMLINES - ROWS;
1765 	    } else {
1766 		CURROW = NUMLINES - 1;
1767 		NEWFV = 0;
1768 	    }
1769 	    USER(NUMLINES - 1) = prompt_wid;
1770 	    p->needredraw = 1;
1771 	}
1772 	if(chtype && (max_pos <= len - 2)) {
1773 	    /* not a control char: we need to fit in the char\n\0 */
1774 	    int i;
1775 	    if(!p->overwrite) {
1776 		for(i = max_pos; i > cur_pos; i--)
1777 		    cur_line[i] = cur_line[i - 1];
1778 	    }
1779 	    cur_line[cur_pos] = cur_char;
1780 	    if(!p->overwrite || cur_pos == max_pos) {
1781 		max_pos += 1;
1782 		cur_line[max_pos] = L'\0';
1783 	    }
1784 	    cur_pos++;
1785 	} else { /* a control char */
1786 	    /* do normal editing commands */
1787 	    int i;
1788 	    switch(cur_char) {
1789 	    case BEGINLINE:
1790 		cur_pos = 0;
1791 		break;
1792 	    case CHARLEFT:
1793 		if(cur_pos > 0) cur_pos--;
1794 		break;
1795 	    case ENDLINE:
1796 		cur_pos = max_pos;
1797 		break;
1798 	    case CHARRIGHT:
1799 		if(cur_pos < max_pos) cur_pos ++;
1800 		break;
1801 	    case KILLRESTOFLINE:
1802 		max_pos = cur_pos;
1803 		cur_line[max_pos] = L'\0';
1804 		break;
1805 	    case KILLLINE:
1806 		max_pos = cur_pos = 0;
1807 		cur_line[max_pos] = L'\0';
1808 		break;
1809 	    case PREVHISTORY:
1810 		P = wgl_hist_prev();
1811 		xbufmakeroom(p->lbuf, wcslen(P) + 1);
1812 		wcscpy(cur_line, P);
1813 		cur_pos = max_pos = wcslen(cur_line);
1814 		break;
1815 	    case NEXTHISTORY:
1816 		P = wgl_hist_next();
1817 		xbufmakeroom(p->lbuf, wcslen(P) + 1);
1818 		wcscpy(cur_line, P);
1819 		cur_pos = max_pos = wcslen(cur_line);
1820 		break;
1821 	    case BACKCHAR:
1822 		if(cur_pos > 0) {
1823 		    cur_pos--;
1824 		    for(i = cur_pos; i <= max_pos - 1; i++)
1825 			cur_line[i] = cur_line[i + 1];
1826 		    max_pos--;
1827 		}
1828 		break;
1829 	    case DELETECHAR:
1830 		if(max_pos == 0) break;
1831 		if(cur_pos < max_pos) {
1832 		    for(i = cur_pos; i <= max_pos - 1; i++)
1833 			cur_line[i] = cur_line[i + 1];
1834 		    max_pos--;
1835 		}
1836 		break;
1837 	    case CHARTRANS:
1838 		if(cur_pos < 1) break;
1839 		if(cur_pos >= max_pos) break;
1840 		cur_char = cur_line[cur_pos];
1841 		cur_line[cur_pos] = cur_line[cur_pos-1];
1842 		cur_line[cur_pos-1] = cur_char;
1843 		break;
1844 	    default:   /* Another control char, or overflow */
1845 		if (chtype || (cur_char == L'\n') || (cur_char == EOFKEY)) {
1846 		    if (chtype) {
1847 			if (cur_pos == max_pos) {
1848 			    consoleunputc(c);
1849 			} else {
1850 			    gabeep();
1851 			    break;
1852 			}
1853 		    }
1854 		    if((cur_char == L'\n') || (cur_char == EOFKEY)) {
1855 			cur_line[max_pos] = L'\n';
1856 			cur_line[max_pos + 1] = L'\0';
1857 		    } else
1858 			cur_line[max_pos] = L'\0';
1859 		    wcstobuf(buf, len, cur_line);
1860 		    //sprintf(buf, "%ls", cur_line);
1861 		    //if(strlen(buf) == 0) strcpy(buf, "invalid input\n");
1862 		    CURROW = -1;
1863 		    cur_line[max_pos] = L'\0';
1864 		    if (max_pos && addtohistory) wgl_histadd(cur_line);
1865 		    xbuffixl(p->lbuf);
1866 		    consolewrites(c, "\n");
1867 		    showcaret(c, 0);
1868 		    REDRAW;
1869 		    return cur_char == EOFKEY;
1870 		}
1871 		break;
1872 	    }
1873 	}
1874 	draweditline(c);
1875     }
1876 }
1877 
console_sbf(control c,int pos)1878 void console_sbf(control c, int pos)
1879 {
1880     ConsoleData p = getdata(c);
1881 
1882     if (pos < 0) {
1883 	pos = -pos - 1 ;
1884 	if (FC != pos) setfirstcol(c, pos);
1885     } else
1886 	if (FV != pos) setfirstvisible(c, pos);
1887 }
1888 
console_im(control c,font * f,point * pt)1889 void console_im(control c, font *f, point *pt)
1890 {
1891   ConsoleData p = getdata(c);
1892   pt->x = BORDERX + CURCOL * FW;
1893   pt->y = BORDERY + CURROW * FH;
1894   *f = consolefn;
1895 }
1896 
1897 void Rconsolesetwidth(int);
1898 int setWidthOnResize = 0;
1899 
consolecols(console c)1900 int consolecols(console c)
1901 {
1902     ConsoleData p = getdata(c);
1903 
1904     return COLS;
1905 }
1906 
consoleresize(console c,rect r)1907 void consoleresize(console c, rect r)
1908 {
1909     ConsoleData p = getdata(c);
1910 
1911     int rr, pcols = COLS;
1912 
1913     if (((WIDTH  == r.width) &&
1914 	 (HEIGHT == r.height)) ||
1915 	(r.width == 0) || (r.height == 0) ) /* minimize */
1916 	return;;
1917 /*
1918  *  set first visible to keep the bottom line on a console,
1919  *  the middle line on a pager
1920  */
1921     if (p->kind == CONSOLE) rr = FV + ROWS;
1922     else rr = FV + ROWS/2;
1923     ROWS = r.height/FH - 1;
1924     if (p->kind == CONSOLE) rr -= ROWS;
1925     else rr -= ROWS/2;
1926     COLS = r.width/FW - 1;
1927     WIDTH = r.width;
1928     HEIGHT = r.height;
1929     BORDERX = (WIDTH - COLS*FW) / 2;
1930     BORDERY = (HEIGHT - ROWS*FH) / 2;
1931     NEWFV = NUMLINES - ROWS;
1932     if (NEWFV < 0) NEWFV = 0;
1933     del(BM);
1934     BM = newbitmap(r.width, r.height, 2);
1935     if (!BM) {
1936        R_ShowMessage(G_("Insufficient memory. Please close the console"));
1937        return ;
1938     }
1939     if(!p->lbuf) return;;    /* don't implement resize if no content
1940 				   yet in pager */
1941     if (CURROW >= 0) {
1942 	if (NUMLINES > ROWS) {
1943 	    CURROW = ROWS - 1;
1944 	} else
1945 	    CURROW = NUMLINES - 1;
1946     }
1947     clear(c);
1948     p->needredraw = 1;
1949     setfirstvisible(c, rr);
1950     if (setWidthOnResize && p->kind == CONSOLE && COLS != pcols)
1951 	Rconsolesetwidth(COLS);
1952 }
1953 
consolesetbrk(console c,actionfn fn,char ch,char mod)1954 void consolesetbrk(console c, actionfn fn, char ch, char mod)
1955 {
1956     ConsoleData p = getdata(c);
1957 
1958     p->chbrk = ch;
1959     p->modbrk = mod;
1960     p->fbrk = fn;
1961 }
1962 
1963 font consolefn = NULL;
1964 char fontname[LF_FACESIZE+4];
1965 int fontsty, pointsize;
1966 int consoler = 25, consolec = 80, consolex = 0, consoley = 0;
1967 int pagerrow = 25, pagercol = 80;
1968 int pagerMultiple = 1, haveusedapager = 0;
1969 int consolebufb = DIMLBUF, consolebufl = MLBUF, consolebuffered = 1;
1970 static int consoleblink = 1;
1971 
1972 void
setconsoleoptions(const char * fnname,int fnsty,int fnpoints,int rows,int cols,int consx,int consy,rgb * nguiColors,int pgr,int pgc,int multiplewindows,int widthonresize,int bufbytes,int buflines,int buffered,int cursor_blink)1973 setconsoleoptions(const char *fnname,int fnsty, int fnpoints,
1974 		  int rows, int cols, int consx, int consy,
1975 		  rgb *nguiColors,
1976 		  int pgr, int pgc, int multiplewindows, int widthonresize,
1977 		  int bufbytes, int buflines, int buffered, int cursor_blink)
1978 {
1979     char msg[LF_FACESIZE + 128];
1980     strncpy(fontname, fnname, LF_FACESIZE);
1981     fontname[LF_FACESIZE] = L'\0';
1982     fontsty =   fnsty;
1983     pointsize = fnpoints;
1984     if (consolefn) del(consolefn);
1985     consolefn = NULL;
1986     if (strcmp(fontname, "FixedFont")) {
1987 	consolefn = gnewfont(NULL, fnname, fnsty | FixedWidth, fnpoints, 0.0, 1);
1988 	if (!consolefn) {
1989 	    /* This is unlikely to happen: it will find some match */
1990 	    snprintf(msg, LF_FACESIZE + 128,
1991 		     G_("Font %s-%d-%d  not found.\nUsing system fixed font"),
1992 		     fontname, fontsty | FixedWidth, pointsize);
1993 	    R_ShowMessage(msg);
1994 	    consolefn = FixedFont;
1995 	}
1996     }
1997 /*    if (!ghasfixedwidth(consolefn)) {
1998        sprintf(msg,
1999 	       "Font %s-%d-%d has variable width.\nUsing system fixed font.",
2000 	       fontname, fontsty, pointsize);
2001        R_ShowMessage(msg);
2002        consolefn = FixedFont;
2003        } */
2004     consoler = rows;
2005     consolec = cols;
2006     consolex = consx;
2007     consoley = consy;
2008     for (int i=0; i<numGuiColors; i++)
2009 	guiColors[i] = nguiColors[i];
2010     pagerrow = pgr;
2011     pagercol = pgc;
2012     pagerMultiple = multiplewindows;
2013     setWidthOnResize = widthonresize;
2014     consolebufb = bufbytes;
2015     consolebufl = buflines;
2016     consolebuffered = buffered;
2017     consoleblink = cursor_blink;
2018 }
2019 
consoleprint(console c)2020 void consoleprint(console c)
2021 {
2022     ConsoleData p = getdata(c);
2023 
2024 
2025     printer lpr;
2026     int cc, rr, fh, cl, cp, clinp, i;
2027     int top, left;
2028     int x0, y0, x1, y1;
2029     font f;
2030     wchar_t *s = L"";
2031     char msg[LF_FACESIZE + 128], title[60];
2032     wchar_t buf[1024];
2033     cursor cur;
2034     if (!(lpr = newprinter(0.0, 0.0, ""))) return;;
2035     show(c);
2036 /*
2037  * If possible, we avoid to use FixedFont for printer since it hasn't the
2038  * right size
2039  */
2040     f = gnewfont(lpr, strcmp(fontname, "FixedFont") ? fontname : "Courier New",
2041 		 fontsty, pointsize, 0.0, 1);
2042     if (!f) {
2043 	/* Should not happen but....*/
2044 	snprintf(msg, LF_FACESIZE + 128,
2045 		 G_("Font %s-%d-%d  not found.\nUsing system fixed font"),
2046 		 strcmp(fontname, "FixedFont") ? fontname : "Courier New",
2047 		 fontsty, pointsize);
2048 	R_ShowMessage(msg);
2049 	f = FixedFont;
2050     }
2051     top = devicepixelsy(lpr) / 5;
2052     left = devicepixelsx(lpr) / 5;
2053     fh = fontheight(f);
2054     rr = getheight(lpr) - top;
2055     cc = getwidth(lpr) - 2*left;
2056     strncpy(title, GA_gettext(c), 59);
2057     if (strlen(GA_gettext(c)) > 59) strcpy(&title[56], "...");
2058     cur = currentcursor();
2059     setcursor(WatchCursor);
2060 
2061     /* Look for a selection */
2062     if (p->sel) {
2063 	int len, c1, c2, c3;
2064 	if (p->my0 >= NUMLINES) p->my0 = NUMLINES - 1;
2065 	if (p->my0 < 0) p->my0 = 0;
2066 	len = wcslen(LINE(p->my0));
2067 	if (p->mx0 >= len) p->mx0 = len - 1;
2068 	if (p->mx0 < 0) p->mx0 = 0;
2069 	if (p->my1 >= NUMLINES) p->my1 = NUMLINES - 1;
2070 	if (p->my1 < 0) p->my1 = 0;
2071 	len = wcslen(LINE(p->my1));
2072 	if (p->mx1 >= len) p->mx1 = len - 1;
2073 	if (p->mx1 < 0) p->mx1 = 0;
2074 	c1 = (p->my0 < p->my1);
2075 	c2 = (p->my0 == p->my1);
2076 	c3 = (p->mx0 < p->mx1);
2077 	if (c1 || (c2 && c3)) {
2078 	    x0 = p->mx0; y0 = p->my0;
2079 	    x1 = p->mx1; y1 = p->my1;
2080 	}
2081 	else {
2082 	    x0 = p->mx1; y0 = p->my1;
2083 	    x1 = p->mx0; y1 = p->my0;
2084 	}
2085     } else {
2086 	x0 = y0 = 0;
2087 	y1 = NUMLINES - 1;
2088 	x1 = wcslen(LINE(y1));
2089     }
2090 
2091     cl = y0; /* current line */
2092     clinp = rr;
2093     cp = 1; /* current page */
2094 
2095     /* s is possible continuation line */
2096     while ((cl <= y1) || (*s)) {
2097 	if (clinp + fh >= rr) {
2098 	    if (cp > 1) nextpage(lpr);
2099 	    gdrawstr(lpr, f, Black, pt(left, top), title);
2100 	    snprintf(msg, LF_FACESIZE + 128, "Page %d", cp++);
2101 	    gdrawstr(lpr, f, Black,
2102 		     pt(cc - gstrwidth(lpr, f, msg) - 1, top),
2103 		     msg);
2104 	    clinp = top + 2 * fh;
2105 	}
2106 	if (!*s) {
2107 	    if (cl == y0) s = LINE(cl++) + x0;
2108 	    else if (cl < y1) s = LINE(cl++);
2109 	    else if (cl == y1) {
2110 		s = wcsncpy(buf, LINE(cl++), 1023);
2111 		s[min(x1, 1023) + 1] = L'\0';
2112 	    } else break;
2113 	}
2114 	if (!*s) {
2115 	    clinp += fh;
2116 	} else {
2117 	    wchar_t lc = L'\0';
2118 	    for (i = wcslen(s); i > 0; i--) {
2119 		lc = s[i];
2120 		s[i] = L'\0';
2121 		if (gwcswidth(lpr, f, s) < cc) break;
2122 		s[i] = lc;
2123 	    }
2124 	    gdrawwcs(lpr, f, Black, pt(left, clinp), s);
2125 	    clinp += fh;
2126 	    s[i] = lc;
2127 	    s = s + i;
2128 	}
2129     }
2130 
2131     if (f != FixedFont) del(f);
2132     del(lpr);
2133     setcursor(cur);
2134 }
2135 
2136 FILE *R_wfopen(const wchar_t *filename, const wchar_t *mode);
2137 
consolesavefile(console c,int pager)2138 void consolesavefile(console c, int pager)
2139 {
2140     ConsoleData p = getdata(c);
2141 
2142     wchar_t *fn;
2143     cursor cur;
2144     FILE *fp;
2145     int x0, y0, x1, y1, cl;
2146     wchar_t *s, buf[1024];
2147 
2148     setuserfilterW(L"Text files (*.txt)\0*.txt\0All files (*.*)\0*.*\0\0");
2149     if(p->sel)
2150 	fn = askfilesaveW(G_("Save selection to"), "lastsave.txt");
2151     else
2152 	fn = askfilesaveW(G_("Save console contents to"), "lastsave.txt");
2153     show(c);
2154     if (fn) {
2155 	fp = R_wfopen(fn, L"wt");
2156 	if (!fp) return;
2157 	cur = currentcursor();
2158 	setcursor(WatchCursor);
2159 
2160 	/* Look for a selection */
2161 	if (p->sel) {
2162 	    int len, c1, c2, c3;
2163 	    if (p->my0 >= NUMLINES) p->my0 = NUMLINES - 1;
2164 	    if (p->my0 < 0) p->my0 = 0;
2165 	    len = wcslen(LINE(p->my0));
2166 	    if (p->mx0 >= len) p->mx0 = len - 1;
2167 	    if (p->mx0 < 0) p->mx0 = 0;
2168 	    if (p->my1 >= NUMLINES) p->my1 = NUMLINES - 1;
2169 	    if (p->my1 < 0) p->my1 = 0;
2170 	    len = wcslen(LINE(p->my1));
2171 	    if (p->mx1 >= len) p->mx1 = len - 1;
2172 	    if (p->mx1 < 0) p->mx1 = 0;
2173 	    c1 = (p->my0 < p->my1);
2174 	    c2 = (p->my0 == p->my1);
2175 	    c3 = (p->mx0 < p->mx1);
2176 	    if (c1 || (c2 && c3)) {
2177 		x0 = p->mx0; y0 = p->my0;
2178 		x1 = p->mx1; y1 = p->my1;
2179 	    }
2180 	    else {
2181 		x0 = p->mx1; y0 = p->my1;
2182 		x1 = p->mx0; y1 = p->my0;
2183 	    }
2184 	} else {
2185 	    x0 = y0 = 0;
2186 	    y1 = NUMLINES - 1;
2187 	    x1 = wcslen(LINE(y1));
2188 	}
2189 
2190 	for (cl = y0; cl <= y1; cl++) {
2191 	    if (cl == y0) s = LINE(cl) + x0;
2192 	    else if (cl < y1) s = LINE(cl);
2193 	    else if (cl == y1) {
2194 		s = wcsncpy(buf, LINE(cl), 1023);
2195 		s[min(x1, 1023) + 1] = L'\0';
2196 	    } else break;
2197 	    fputws(s, fp); fputc('\n', fp);
2198 	}
2199 	fclose(fp);
2200 	setcursor(cur);
2201     }
2202 }
2203 
2204 
newconsole(char * name,int flags)2205 console newconsole(char *name, int flags)
2206 {
2207     console c;
2208     ConsoleData p;
2209 
2210     p = newconsoledata((consolefn) ? consolefn : FixedFont,
2211 		       consoler, consolec, consolebufb, consolebufl,
2212 		       guiColors,
2213 		       CONSOLE, consolebuffered, consoleblink);
2214     if (!p) return NULL;
2215     c = (console) newwindow(name, rect(consolex, consoley, WIDTH, HEIGHT),
2216 			    flags | TrackMouse | VScrollbar | HScrollbar);
2217     HEIGHT = getheight(c);
2218     WIDTH  = getwidth(c);
2219     COLS = WIDTH / FW - 1;
2220     ROWS = HEIGHT / FH - 1;
2221     gsetcursor(c, ArrowCursor);
2222     gchangescrollbar(c, VWINSB, 0, 0, ROWS, 1);
2223     gchangescrollbar(c, HWINSB, 0, COLS-1, COLS, 1);
2224     BORDERX = (WIDTH - COLS*FW) / 2;
2225     BORDERY = (HEIGHT - ROWS*FH) / 2;
2226     setbackground(c, guiColors[consolebg]);
2227     BM = newbitmap(WIDTH, HEIGHT, 2);
2228     if (!c || !BM ) {
2229 	freeConsoleData(p);
2230 	del(c);
2231 	return NULL;
2232     }
2233     setdata(c, p);
2234     sethit(c, console_sbf);
2235     setresize(c, consoleresize);
2236     setredraw(c, drawconsole);
2237     setdel(c, delconsole);
2238     setkeyaction(c, console_ctrlkeyin);
2239     setkeydown(c, console_normalkeyin);
2240     setmousedrag(c, console_mousedrag);
2241     setmouserepeat(c, console_mouserep);
2242     setmousedown(c, console_mousedown);
2243     setim(c, console_im);
2244     return(c);
2245 }
2246 
consolehelp()2247 void  consolehelp()
2248 {
2249     char s[4096];
2250 
2251     strcpy(s,G_("Scrolling.\n"));
2252     strcat(s,G_("  Keyboard: PgUp, PgDown, Ctrl+Arrows, Ctrl+Home, Ctrl+End,\n"));
2253     strcat(s,G_("  Mouse: use the scrollbar(s).\n\n"));
2254     strcat(s,G_("Editing.\n"));
2255     strcat(s,G_("  Moving the cursor: \n"));
2256     strcat(s,G_("     Left arrow or Ctrl+B: move backward one character;\n"));
2257     strcat(s,G_("     Right arrow or Ctrl+F: move forward one character;\n"));
2258     strcat(s,G_("     Home or Ctrl+A: go to beginning of line;\n"));
2259     strcat(s,G_("     End or Ctrl+E: go to end of line;\n"));
2260     strcat(s,G_("  History: Up and Down Arrows, Ctrl+P, Ctrl+N\n"));
2261     strcat(s,G_("  Deleting:\n"));
2262     strcat(s,G_("     Del or Ctrl+D: delete current character or selection;\n"));
2263     strcat(s,G_("     Backspace: delete preceding character;\n"));
2264     strcat(s,G_("     Ctrl+Del or Ctrl+K: delete text from current character to end of line.\n"));
2265     strcat(s,G_("     Ctrl+U: delete all text from current line.\n"));
2266     strcat(s,G_("  Copy and paste.\n"));
2267     strcat(s,G_("     Use the mouse (with the left button held down) to mark (select) text.\n"));
2268     strcat(s,G_("     Use Shift+Del (or Ctrl+C) to copy the marked text to the clipboard and\n"));
2269     strcat(s,G_("     Shift+Ins (or Ctrl+V or Ctrl+Y) to paste the content of the clipboard (if any)  \n"));
2270     strcat(s,G_("     to the console, Ctrl+X first copy then paste\n"));
2271     strcat(s,G_("  Misc:\n"));
2272     strcat(s,G_("     Ctrl+L: Clear the console.\n"));
2273     strcat(s,G_("     Ctrl+O or INS: Toggle overwrite mode: initially off.\n"));
2274     strcat(s,G_("     Ctrl+T: Interchange current char with one to the left.\n"));
2275     strcat(s,G_("\nNote: Console is updated only when some input is required.\n"));
2276     strcat(s,G_("  Use Ctrl+W to toggle this feature off/on.\n\n"));
2277     strcat(s,G_("Use ESC to stop the interpreter.\n\n"));
2278     strcat(s,G_("TAB starts completion of the current word.\n\n"));
2279     strcat(s,G_("Standard Windows hotkeys can be used to switch to the\n"));
2280     strcat(s,G_("graphics device (Ctrl+Tab or Ctrl+F6 in MDI, Alt+Tab in SDI)"));
2281     askok(s);
2282 }
2283 
consoleclear(control c)2284 void consoleclear(control c)
2285 {
2286     ConsoleData p = getdata(c);
2287 
2288     xbuf l = p->lbuf;
2289     int oldshift = l->shift;
2290 
2291     l->shift = (l->ns - 1);
2292     xbufshift(l);
2293     l->shift = oldshift;
2294     NEWFV = 0;
2295     CURROW = 0;
2296     REDRAW;
2297 }
2298