1 /*
2  * w32cbrd:  collection of common clipboard manipulation routines shared by
3  *           the Win32 console- and GUI-based vile editor.
4  *
5  * Caveats
6  * =======
7  * -- On a stock Win95 host, the first copy to the clipboard from the
8  *    console version of vile causes the busy thread cursor to be displayed
9  *    (i.e., cursor changes to a pointer/hourglass icon).  This cursor stays
10  *    active for 5-10 seconds (all apps are active) and then goes away.
11  *    Subsequent copies do not show this cursor.  On an NT host, this
12  *    phenomenon does not occur.
13  *
14  * $Id: w32cbrd.c,v 1.44 2020/01/17 22:29:27 tom Exp $
15  */
16 
17 #include "estruct.h"
18 #include "edef.h"
19 
20 #include <stdlib.h>
21 #include <search.h>
22 
23 #if defined(UNICODE) && defined(CF_UNICODETEXT)
24 #define USE_UNICODE 1
25 #else
26 #define USE_UNICODE 0
27 #endif
28 
29 #if USE_UNICODE
30 #define myTextFormat CF_UNICODETEXT
31 #else
32 #define myTextFormat CF_TEXT
33 #endif
34 
35 #define  CLIPBOARD_BUSY      "[Clipboard currently busy]"
36 #define  CLIPBOARD_COPY_MB   "[Clipboard copy from minibuffer not supported]"
37 #define  CLIPBOARD_COPYING   "[Copying...]"
38 #define  CLIPBOARD_COPY_FAIL "[Clipboard copy failed]"
39 #define  CLIPBOARD_COPY_MEM  "[Insufficient memory for copy operation]"
40 
41 typedef struct rgn_cpyarg_struct {
42     UINT nbyte, nline;
43     W32_CHAR *dst;
44 } RGN_CPYARG;
45 
46 static int print_low, print_high;
47 
48 /* ------------------------------------------------------------------ */
49 
50 static void
minibuffer_abort(void)51 minibuffer_abort(void)
52 {
53     W32_CHAR str[3];
54 
55     /*
56      * Aborting out of the minibuffer is not easy.  When in doubt, use a
57      * sledge hammer.
58      */
59 
60     str[0] = ESC;
61     str[1] = '\0';
62     (void) w32_keybrd_write(str);
63     update(TRUE);
64 }
65 
66 static void
report_cbrdstats(UINT nbyte,UINT nline,int pasted)67 report_cbrdstats(UINT nbyte, UINT nline, int pasted)
68 {
69     char buf[128];
70 
71     if (!global_b_val(MDTERSE)) {
72 	lsprintf(buf,
73 		 "[%s %d line%s, %d bytes %s clipboard]",
74 		 (pasted) ? "Pasted" : "Copied",
75 		 nline,
76 		 PLURAL(nline),
77 		 nbyte,
78 		 (pasted) ? "from" : "to");
79 	mlwrite(buf);
80     } else
81 	mlforce("[%d lines]", nline);
82 }
83 
84 /* The memory block handle _must_ be unlocked before calling this fn. */
85 static int
setclipboard(HGLOBAL hClipMem,UINT nbyte,UINT nline)86 setclipboard(HGLOBAL hClipMem, UINT nbyte, UINT nline)
87 {
88     int rc, i;
89 
90     TRACE((T_CALLED "setclipboard(%p, %d, %d)\n", hClipMem, nbyte, nline));
91 
92     for (rc = i = 0; i < 8 && (!rc); i++) {
93 	/* Try to open clipboard */
94 
95 	if (!OpenClipboard(NULL))
96 	    Sleep(500);
97 	else
98 	    rc = 1;
99     }
100     if (!rc) {
101 	mlforce(CLIPBOARD_BUSY);
102 	GlobalFree(hClipMem);
103 	returnCode(FALSE);
104     }
105     EmptyClipboard();
106     rc = (SetClipboardData(myTextFormat, hClipMem) != NULL);
107     CloseClipboard();
108     if (!rc) {
109 	mlforce(CLIPBOARD_COPY_FAIL);
110 	GlobalFree(hClipMem);
111     } else {
112 	/* success */
113 
114 	report_cbrdstats(nbyte - 1,	/* subtract terminating NUL */
115 			 nline,
116 			 FALSE);
117     }
118     returnCode(rc);
119 }
120 
121 /* Count lines and nonbuffer data added during "copy to clipboard" operation. */
122 static void
cbrd_count_meta_data(int len,UINT * nbyte,UINT * nline,char * src)123 cbrd_count_meta_data(int len,
124 		     UINT * nbyte,
125 		     UINT * nline,
126 		     char *src)
127 {
128     register UINT c;
129 
130     while (len--) {
131 	if ((c = (UCHAR) * src++) == '\n') {
132 	    (*nline)++;
133 	    (*nbyte)++;		/* API requires CR/LF terminator */
134 	} else if (c < _SPC_ && c != _TAB_)	/* assumes ASCII char set        */
135 	    (*nbyte)++;		/* account for '^' meta char     */
136 	else if (c > _TILDE_ && (!PASS_HIGH(c)))	/* assumes ASCII char set */
137 	    (*nbyte) += 3;	/* account for '\xdd' meta chars */
138     }
139 }
140 
141 /*
142  * This function is called to process each logical line of data in a
143  * user-selected region.  It counts the number of bytes of data in the line.
144  */
145 static int
count_rgn_data(void * argp,int l,int r)146 count_rgn_data(void *argp, int l, int r)
147 {
148     RGN_CPYARG *cpyp;
149     int len;
150     LINE *lp;
151 
152     lp = DOT.l;
153 
154     /* Rationalize offsets */
155     if (llength(lp) < l)
156 	return (TRUE);
157     if (r > llength(lp))
158 	r = llength(lp);
159     cpyp = argp;
160     if (r == llength(lp) || regionshape == rgn_RECTANGLE) {
161 	/* process implied newline */
162 
163 	cpyp->nline++;
164 	cpyp->nbyte += 2;	/* CBRD API maps NL -> CR/LF */
165     }
166     len = r - l;
167     cpyp->nbyte += len;
168     cbrd_count_meta_data(len, &cpyp->nbyte, &cpyp->nline, lvalue(lp) + l);
169     return (TRUE);
170 }
171 
172 static void
cbrd_copy_and_xlate(int len,W32_CHAR ** cbrd_ptr,UCHAR * src)173 cbrd_copy_and_xlate(int len, W32_CHAR ** cbrd_ptr, UCHAR * src)
174 {
175     register UINT c;
176     W32_CHAR *dst = *cbrd_ptr;
177     UCHAR *last = (len + src);
178 
179     while (src < last) {
180 	if ((c = *src) == '\n') {
181 	    *dst++ = '\r';
182 	    *dst++ = '\n';
183 	}
184 #if USE_UNICODE
185 	else {
186 	    UINT target;
187 	    int rc = vl_conv_to_utf32(&target, (const char *) src, len);
188 
189 	    if (rc > 1) {
190 		*dst++ = (W32_CHAR) target;
191 		src += ((size_t) rc - 1);
192 	    } else {
193 		*dst++ = (W32_CHAR) * src;
194 	    }
195 	}
196 #else
197 	else if ((c >= _SPC_ && c <= _TILDE_) || (c == _TAB_))
198 	    *dst++ = (UCHAR) c;
199 	else if (c < _SPC_) {
200 	    *dst++ = '^';
201 	    *dst++ = ctrldigits[c];
202 	} else {
203 	    /* c > _TILDE_ */
204 
205 	    if (!PASS_HIGH(c)) {
206 		*dst++ = '\\';
207 		*dst++ = 'x';
208 		*dst++ = hexdigits[(c & 0xf0) >> 4];
209 		*dst++ = hexdigits[c & 0xf];
210 	    } else
211 		*dst++ = (W32_CHAR) c;
212 	}
213 #endif
214 	++src;
215     }
216     *cbrd_ptr = dst;
217 }
218 
219 /*
220  * This function is called to process each logical line of data in a
221  * user-selected region.  It copies region data to a buffer allocated on
222  * the heap.
223  */
224 static int
copy_rgn_data(void * argp,int l,int r)225 copy_rgn_data(void *argp, int l, int r)
226 {
227     RGN_CPYARG *cpyp;
228     int len;
229     LINE *lp;
230 
231     lp = DOT.l;
232 
233     /* Rationalize offsets */
234     if (llength(lp) < l)
235 	return (TRUE);
236     if (r > llength(lp))
237 	r = llength(lp);
238     cpyp = argp;
239     len = r - l;
240     cbrd_copy_and_xlate(len, &cpyp->dst, (UCHAR *) (lvalue(lp) + l));
241     if (r == llength(lp) || regionshape == rgn_RECTANGLE) {
242 	/* process implied newline */
243 
244 	*cpyp->dst++ = '\r';
245 	*cpyp->dst++ = '\n';
246     }
247     return (TRUE);
248 }
249 
250 /*
251  * Copy contents of [un]named register to Windows clipboard.  The control
252  * flow is shamelessly copied from kwrite().
253  */
254 static int
cbrd_reg_copy(void)255 cbrd_reg_copy(void)
256 {
257     HGLOBAL hClipMem;
258     register int i;
259     KILL *kp;			/* pointer into [un]named register */
260     UINT nbyte;
261     UINT nline;
262     UCHAR *copy;
263     int rc;
264 
265     TRACE((T_CALLED "cbrd_reg_copy()\n"));
266     /* make sure there is something to put */
267     if (kbs[ukb].kbufh == NULL) {
268 	mlforce("[Nothing to copy]");
269 	returnCode(FALSE);	/* not an error, just nothing */
270     }
271 
272     print_high = global_g_val(GVAL_PRINT_HIGH);
273     print_low = global_g_val(GVAL_PRINT_LOW);
274 
275     /* tell us we're writing */
276     mlwrite(CLIPBOARD_COPYING);
277     nline = 0;
278     nbyte = 0;
279 
280     /*
281      * Make 2 passes over the data.  1st pass counts the data and
282      * adjusts for the fact that:
283      *
284      * 1) each '\n' must be warped to "\r\n" to satisfy clipboard APIs.
285      * 2) unprintable data (modulo tabs) must be warped to a printable
286      *    equivalent.
287      */
288     for (kp = kbs[ukb].kbufh; kp; kp = kp->d_next) {
289 	i = KbSize(ukb, kp);
290 	nbyte += i;
291 	cbrd_count_meta_data(i, &nbyte, &nline, (char *) kp->d_chunk);
292     }
293     nbyte++;			/* Add room for terminating null */
294 
295     /* 2nd pass -- alloc storage for data and copy to clipboard. */
296     TRACE(("copying %u bytes to clipboard\n", nbyte));
297     hClipMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, nbyte * sizeof(W32_CHAR));
298     if (hClipMem == NULL) {
299 	mlforce(CLIPBOARD_COPY_MEM);
300 	rc = FALSE;
301     } else {
302 	copy = malloc(nbyte);
303 	if (copy != 0) {
304 	    UCHAR *tmp = copy;
305 	    W32_CHAR *dst;
306 	    if ((dst = GlobalLock(hClipMem)) != NULL) {
307 		W32_CHAR *old = dst;
308 		for (kp = kbs[ukb].kbufh; kp; kp = kp->d_next) {
309 		    int size = KbSize(ukb, kp);
310 		    memcpy(tmp, kp->d_chunk, size);
311 		    tmp += size;
312 		}
313 		cbrd_copy_and_xlate((int) (tmp - copy), &dst, copy);
314 		*dst = '\0';
315 		GlobalUnlock(hClipMem);
316 		rc = setclipboard(hClipMem, (UINT) (dst - old), nline);
317 	    } else {
318 		mlforce("[Cannot copy]");
319 		rc = FALSE;
320 	    }
321 	    free(copy);
322 	} else {
323 	    mlforce("[Cannot copy]");
324 	    rc = FALSE;
325 	}
326     }
327     returnCode(rc);
328 }
329 
330 /*
331  * Copy contents of unnamed register to Windows clipboard.
332  *
333  * Bound to Alt+Insert.
334  */
335 int
cbrdcpy_unnamed(int unused1 GCC_UNUSED,int unused2 GCC_UNUSED)336 cbrdcpy_unnamed(int unused1 GCC_UNUSED, int unused2 GCC_UNUSED)
337 {
338     int rc;
339 
340     if (reading_msg_line) {
341 	minibuffer_abort();	/* FIXME -- goes away some day? */
342 	mlerase();
343 	mlforce(CLIPBOARD_COPY_MB);
344 	return (ABORT);
345     }
346     kregcirculate(FALSE);
347     rc = cbrd_reg_copy();
348     ukb = 0;
349     return (rc);
350 }
351 
352 /*
353  * Copy the currently-selected region (i.e., the range of lines from DOT to
354  * MK, inclusive) to the windows clipboard.  Lots of code has been borrowed
355  * and/or adapted from operyank() and writereg().
356  */
357 static int
cbrdcpy_region(void)358 cbrdcpy_region(void)
359 {
360     RGN_CPYARG cpyarg;
361     DORGNLINES dorgn;
362     HGLOBAL hClipMem;
363     MARK odot;
364     int rc;
365 
366     TRACE((T_CALLED "cbrdcpy_region()\n"));
367     mlwrite(CLIPBOARD_COPYING);
368     print_high = global_g_val(GVAL_PRINT_HIGH);
369     print_low = global_g_val(GVAL_PRINT_LOW);
370     odot = DOT;			/* do_lines_in_region() moves DOT. */
371     cpyarg.nbyte = cpyarg.nline = 0;
372     dorgn = get_do_lines_rgn();
373 
374     /*
375      * Make 2 passes over the data.  1st pass counts the data and
376      * adjusts for the fact that:
377      *
378      * 1) each '\n' must be warped to "\r\n" to satisfy clipboard APIs.
379      * 2) unprintable data (modulo tabs) must be warped to a printable
380      *    equivalent.
381      */
382     rc = dorgn(count_rgn_data, &cpyarg, TRUE);
383     DOT = odot;
384     if (!rc)
385 	returnCode(FALSE);
386     cpyarg.nbyte++;		/* Terminating nul */
387 
388     /* 2nd pass -- alloc storage for data and copy to clipboard. */
389     hClipMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, cpyarg.nbyte);
390     if (hClipMem == NULL) {
391 	mlforce(CLIPBOARD_COPY_MEM);
392 	returnCode(FALSE);
393     }
394     if ((cpyarg.dst = GlobalLock(hClipMem)) != NULL) {
395 	/*
396 	 * Pass #2 -> The actual copy.  Don't need to restore DOT, that
397 	 * is handled by opercbrdcpy().
398 	 */
399 	rc = dorgn(copy_rgn_data, &cpyarg, TRUE);
400 	GlobalUnlock(hClipMem);
401     } else {
402 	rc = FALSE;
403     }
404     if (!rc) {
405 	GlobalFree(hClipMem);
406 	returnCode(FALSE);
407     }
408     *cpyarg.dst = '\0';
409     returnCode(setclipboard(hClipMem, cpyarg.nbyte, cpyarg.nline));
410 }
411 
412 /*
413  * Copy contents of specified region or register to Windows clipboard.
414  * This command is an operator and mimics the functionality of ^W, but
415  * mimics operyank()'s implementation.
416  *
417  * Bound to Ctrl+Insert.
418  */
419 int
opercbrdcpy(int f,int n)420 opercbrdcpy(int f, int n)
421 {
422     if (reading_msg_line) {
423 	minibuffer_abort();	/* FIXME -- goes away some day? */
424 	mlerase();
425 	mlforce(CLIPBOARD_COPY_MB);
426 	return (ABORT);
427     }
428     if (ukb != 0)
429 	return (cbrd_reg_copy());
430     else {
431 	MARK odot;
432 	int rc;
433 
434 	odot = DOT;
435 	opcmd = OPDEL;
436 	rc = vile_op(f, n, cbrdcpy_region, "Clipboard copy");
437 	DOT = odot;		/* cursor does not move */
438 	return (rc);
439     }
440 }
441 
442 /* ------------------- Paste Functionality ----------------------- */
443 
444 #define MAX_MAPPED_STR 16	/* fairly conservative :-) */
445 
446 static int map_and_insert(UINT, UINT *);
447 
448 typedef struct {
449     UINT val;
450     char *str;
451 } MAP;
452 
453 /* Keep this table sorted by "val" . */
454 /* *INDENT-OFF* */
455 static MAP cbrdmap[] =
456 {
457     { 0x85, "..."  },
458     { 0x8B, "<"    },
459     { 0x91, "'"    },
460     { 0x92, "'"    },
461     { 0x93, "\""   },
462     { 0x94, "\""   },
463     { 0x96, "-"    },
464     { 0x97, "--"   },
465     { 0x99, "(TM)" },
466     { 0x9B, ">"    },
467     { 0xA6, "|"    },
468     { 0xA9, "(C)"  },
469     { 0xAB, "<<"   },
470     { 0xAD, "-"    },
471     { 0xAE, "(R)"  },
472     { 0xB1, "+/-"  },
473     { 0xBB, ">>"   },
474     { 0xBC, "1/4"  },
475     { 0xBD, "1/2"  },
476     { 0xBE, "3/4"  },
477 };
478 /* *INDENT-ON* */
479 
480 /* --------------------------------------------------------------- */
481 
482 static int
map_compare(const void * elem1,const void * elem2)483 map_compare(const void *elem1, const void *elem2)
484 {
485     return (((const MAP *) elem1)->val - ((const MAP *) elem2)->val);
486 }
487 
488 static int
map_cbrd_char(UINT c,W32_CHAR mapped_rslt[MAX_MAPPED_STR])489 map_cbrd_char(UINT c, W32_CHAR mapped_rslt[MAX_MAPPED_STR])
490 {
491     MAP key, *rslt_p;
492     int nmapped = 0;
493     char *str;
494 
495     key.val = c;
496     rslt_p = bsearch(&key,
497 		     cbrdmap,
498 		     sizeof(cbrdmap) / sizeof(cbrdmap[0]),
499 		     sizeof(cbrdmap[0]),
500 		     map_compare);
501     if (!rslt_p)
502 	mapped_rslt[nmapped++] = (UCHAR) c;
503     else {
504 	for (str = rslt_p->str; *str; str++)
505 	    mapped_rslt[nmapped++] = *str;
506     }
507     mapped_rslt[nmapped] = '\0';
508     return (nmapped);
509 }
510 
511 /* paste a single line from the clipboard to the minibuffer. */
512 static int
paste_to_minibuffer(W32_CHAR * cbrddata)513 paste_to_minibuffer(W32_CHAR * cbrddata)
514 {
515     int rc = TRUE;
516     W32_CHAR *cp = cbrddata;
517 #if !USE_UNICODE
518     W32_CHAR one_char[2];
519     W32_CHAR map_str[MAX_MAPPED_STR + 1];
520 #endif
521 
522     while (*cp) {
523 	if (*cp == '\r' && *(cp + 1) == '\n') {
524 	    *cp = '\0';
525 
526 	    /*
527 	     * Don't allow more than one line of data to be pasted into the
528 	     * minibuffer (to protect the user from seriously bad side
529 	     * effects when s/he pastes in the wrong buffer).  We don't
530 	     * report an error here in an effort to retain compatibility
531 	     * with a couple of "significant" win32 apps (e.g., IE and
532 	     * Outlook) that simply truncate a paste at one line when it
533 	     * only makes sense to take a single line of input.
534 	     */
535 	    break;
536 	}
537 	cp++;
538     }
539 #if USE_UNICODE
540     rc = w32_keybrd_write(cbrddata);
541 #else
542     one_char[1] = '\0';
543     while (*cbrddata && rc) {
544 	if (*cbrddata > _TILDE_) {
545 	    (void) map_cbrd_char(*cbrddata, map_str);
546 	    rc = w32_keybrd_write(map_str);
547 	} else {
548 	    one_char[0] = *cbrddata;
549 	    rc = w32_keybrd_write(one_char);
550 	}
551 	cbrddata++;
552     }
553 #endif
554     return (rc);
555 }
556 
557 #define MAX_UTF8 8		/* buffer-size big enough for any UTF-8 conversion */
558 
559 /*
560  * Paste contents of windows clipboard (if TEXT) to current buffer.
561  * Bound to Shift+Insert.
562  */
563 int
cbrdpaste(int f GCC_UNUSED,int n GCC_UNUSED)564 cbrdpaste(int f GCC_UNUSED, int n GCC_UNUSED)
565 {
566     UINT c;
567     W32_CHAR *data;
568     HANDLE hClipMem;
569     int i, rc, suppressnl;
570     UINT nbyte, nline;
571 
572     TRACE((T_CALLED "cbrdpaste\n"));
573     for (rc = i = 0; i < 8 && (!rc); i++) {
574 	/* Try to open clipboard */
575 
576 	if (!OpenClipboard(NULL))
577 	    Sleep(500);
578 	else
579 	    rc = 1;
580     }
581     if (!rc) {
582 	if (reading_msg_line) {
583 	    minibuffer_abort();	/* FIXME -- goes away some day? */
584 	    rc = ABORT;
585 	} else
586 	    rc = FALSE;
587 	mlforce(CLIPBOARD_BUSY);
588 	returnCode(rc);
589     }
590     if ((hClipMem = GetClipboardData(myTextFormat)) == NULL) {
591 	CloseClipboard();
592 	if (reading_msg_line) {
593 	    minibuffer_abort();	/* FIXME -- goes away some day? */
594 	    rc = ABORT;
595 	} else
596 	    rc = FALSE;
597 	mlforce("[Clipboard empty or not TEXT data]");
598 	returnCode(rc);
599     }
600     if ((data = GlobalLock(hClipMem)) == NULL) {
601 	CloseClipboard();
602 	if (reading_msg_line) {
603 	    minibuffer_abort();	/* FIXME -- goes away some day? */
604 	    rc = ABORT;
605 	} else
606 	    rc = FALSE;
607 	mlforce("[Can't lock clipboard memory]");
608 	returnCode(rc);
609     }
610     if (reading_msg_line) {
611 	rc = paste_to_minibuffer(data);
612 	GlobalUnlock(hClipMem);
613 	CloseClipboard();
614 	returnCode(rc);
615     }
616     mlwrite(CLIPBOARD_COPYING);
617     nbyte = nline = 0;
618     rc = TRUE;
619 
620     /*
621      * Before stuffing data in the current buffer, save info regarding dot
622      * and mark.  The dot/mark manipulation code is more or less cribbed
623      * from doput() and PutChar().  Note also that clipboard data is always
624      * copied into the current region as if it were an "exact" shape, which
625      * should be the most intuitive result for Windows users who work with
626      * the clipboard (I hope).
627      */
628     suppressnl = is_header_line(DOT, curbp);
629     (void) setmark();
630     while (*data && rc) {
631 	c = *data;
632 
633 	if (c == '\n') {
634 	    nbyte++;
635 	    nline++;
636 	    rc = lnewline();
637 	} else if (c == '\r' && *(data + 1) == '\n') {
638 	    /* Clipboard end of line delimiter is crlf.  Ignore cr. */
639 	    ;
640 	} else if (!b_is_utfXX(curbp) && (c > _TILDE_)) {
641 	    rc = map_and_insert(c, &nbyte);
642 	} else {
643 	    int chunk;
644 	    int c2;
645 	    int base = DOT.o;
646 #ifdef UNICODE
647 	    int in_chunk;
648 	    int chunk_bytes = 0;
649 	    char *dst;
650 #endif
651 
652 	    for (chunk = 0; data[chunk] != 0; ++chunk) {
653 		if ((c2 = data[chunk]) == '\n'
654 		    || (c2 == '\r' && data[chunk + 1] == '\n')
655 		    || (!b_is_utfXX(curbp) && (c > _TILDE_))) {
656 		    break;
657 		}
658 	    }
659 
660 #ifdef UNICODE
661 	    /* sizeof(W32_CHAR) > 1 */
662 
663 	    /* compute 'chunk_bytes' here based on actual bytes */
664 	    for (in_chunk = 0; in_chunk < chunk; ++in_chunk) {
665 		chunk_bytes += vl_conv_to_utf8((UCHAR *) 0, data[in_chunk], MAX_UTF8);
666 	    }
667 
668 	    rc = lins_bytes(chunk_bytes, (int) c);
669 	    if (!rc)
670 		break;
671 
672 	    nbyte += chunk_bytes;
673 	    for (in_chunk = 0, dst = lvalue(DOT.l) + base;
674 		 in_chunk < chunk;
675 		 ++in_chunk) {
676 		UCHAR target[MAX_UTF8];
677 		int len = vl_conv_to_utf8(target, data[in_chunk], MAX_UTF8);
678 		memcpy(dst, target, len);
679 		dst += len;
680 	    }
681 	    data += ((size_t) chunk - 1);
682 #else
683 	    /* sizeof(W32_CHAR) == 1 */
684 
685 	    rc = lins_bytes(chunk, (int) c);
686 	    if (!rc)
687 		break;
688 
689 	    nbyte += chunk;
690 	    memcpy(lvalue(DOT.l) + base, data, chunk);
691 	    data += (chunk - 1);
692 #endif
693 	}
694 	data++;
695     }
696     if (rc) {
697 	if (nbyte > 0 && (data[-1] == '\n') && suppressnl) {
698 	    /*
699 	     * Last byte inserted was a newline and DOT was originally
700 	     * pointing at the beginning of the buffer(??).  In this
701 	     * situation, lins_bytes() has added an additional newline to the
702 	     * buffer.  Remove it.
703 	     */
704 
705 	    (void) ldel_bytes(1, FALSE);
706 	}
707     }
708     GlobalUnlock(hClipMem);
709     CloseClipboard();
710     if (!rc)
711 	(void) no_memory("cbrdpaste()");
712     else {
713 	/*
714 	 * Success.  Fiddle with dot and mark again (another chunk of doput()
715 	 * code).  "Tha' boy shore makes keen use of cut and paste."
716 	 * Don't swap if inserting - this allows you to continue typing
717 	 * after a paste operation without additional futzing with DOT.
718 	 */
719 
720 	if (!insertmode)
721 	    swapmark();		/* I understand this. */
722 	if (is_header_line(DOT, curbp))
723 	    DOT.l = lback(DOT.l);	/* This is a mystery. */
724 	report_cbrdstats(nbyte, nline, TRUE);
725     }
726     returnCode(rc);
727 }
728 
729 /*
730  * Map selected characters from the ANSI character set to their ASCII
731  * equivalents and insert same in the current buffer.
732  */
733 static int
map_and_insert(UINT c,UINT * nbyte)734 map_and_insert(UINT c,		/* ANSI char to insert   */
735 	       UINT * nbyte	/* total #chars inserted */
736 )
737 {
738     W32_CHAR mapped_str[MAX_MAPPED_STR];
739     int i, nmapped, rc;
740 
741     nmapped = map_cbrd_char(c, mapped_str);
742     *nbyte += nmapped;
743     for (rc = TRUE, i = 0; i < nmapped && rc; i++)
744 	rc = lins_bytes(1, mapped_str[i]);
745     return (rc);
746 }
747 
748 /* --------------------- Cut Functionality ----------------------- */
749 
750 /*
751  * Cut current [win]vile selection to windows clipboard.
752  * Bound to Shift+Delete.
753  */
754 int
cbrdcut(int f GCC_UNUSED,int n GCC_UNUSED)755 cbrdcut(int f GCC_UNUSED, int n GCC_UNUSED)
756 {
757     return (w32_del_selection(TRUE));
758 }
759