1 /*
2  * zle_utils.c - miscellaneous line editor utilities
3  *
4  * This file is part of zsh, the Z shell.
5  *
6  * Copyright (c) 1992-1997 Paul Falstad
7  * All rights reserved.
8  *
9  * Permission is hereby granted, without written agreement and without
10  * license or royalty fees, to use, copy, modify, and distribute this
11  * software and to distribute modified versions of this software for any
12  * purpose, provided that the above copyright notice and the following
13  * two paragraphs appear in all copies of this software.
14  *
15  * In no event shall Paul Falstad or the Zsh Development Group be liable
16  * to any party for direct, indirect, special, incidental, or consequential
17  * damages arising out of the use of this software and its documentation,
18  * even if Paul Falstad and the Zsh Development Group have been advised of
19  * the possibility of such damage.
20  *
21  * Paul Falstad and the Zsh Development Group specifically disclaim any
22  * warranties, including, but not limited to, the implied warranties of
23  * merchantability and fitness for a particular purpose.  The software
24  * provided hereunder is on an "as is" basis, and Paul Falstad and the
25  * Zsh Development Group have no obligation to provide maintenance,
26  * support, updates, enhancements, or modifications.
27  *
28  */
29 
30 #include "zle.mdh"
31 #include "zle_utils.pro"
32 
33 /* Primary cut buffer */
34 
35 /**/
36 struct cutbuffer cutbuf;
37 
38 /* Emacs-style kill buffer ring */
39 
40 /**/
41 struct cutbuffer *kring;
42 /**/
43 int kringsize, kringnum;
44 
45 /* Vi named cut buffers.  0-25 are the named buffers "a to "z, and *
46  * 26-35 are the numbered buffer stack "0 to "9.                   */
47 
48 /**/
49 struct cutbuffer vibuf[36];
50 
51 /* the line before last mod (for undo purposes) */
52 
53 /**/
54 ZLE_STRING_T lastline;
55 /**/
56 int lastlinesz, lastll, lastcs;
57 
58 /* size of line buffer */
59 
60 /**/
61 int linesz;
62 
63 /* make sure that the line buffer has at least sz chars */
64 
65 /**/
66 void
sizeline(int sz)67 sizeline(int sz)
68 {
69     int cursz = (zlemetaline != NULL) ? metalinesz : linesz;
70 
71     while (sz > cursz) {
72 	if (cursz < 256)
73 	    cursz = 256;
74 	else
75 	    cursz *= 4;
76 
77 	if (zlemetaline != NULL) {
78 	    /* One spare character for the NULL */
79 	    zlemetaline = realloc(zlemetaline, cursz + 1);
80 	} else {
81 	    /* One spare character for the NULL, one for the newline */
82 	    zleline =
83 		(ZLE_STRING_T)realloc(zleline,
84 				      (cursz + 2) * ZLE_CHAR_SIZE);
85 	}
86     }
87 
88     if (zlemetaline != NULL)
89 	metalinesz = cursz;
90     else
91 	linesz = cursz;
92 }
93 
94 /*
95  * Insert a character, called from main shell.
96  * Note this always operates on the metafied multibyte version of the
97  * line.
98  */
99 
100 /**/
101 mod_export void
zleaddtoline(int chr)102 zleaddtoline(int chr)
103 {
104     spaceinline(1);
105     zlemetaline[zlemetacs++] = chr;
106 }
107 
108 /*
109  * Convert a line editor character to a possibly multibyte character
110  * in a metafied string.  To be safe buf should have space for at least
111  * 2 * MB_CUR_MAX chars for multibyte mode and 2 otherwise.  Returns the
112  * length of the string added.
113  */
114 
115 /**/
116 int
zlecharasstring(ZLE_CHAR_T inchar,char * buf)117 zlecharasstring(ZLE_CHAR_T inchar, char *buf)
118 {
119 #ifdef MULTIBYTE_SUPPORT
120     int ret;
121     char *ptr;
122 
123 #ifdef __STDC_ISO_10646__
124     if (ZSH_INVALID_WCHAR_TEST(inchar)) {
125 	buf[0] = ZSH_INVALID_WCHAR_TO_CHAR(inchar);
126 	ret = 1;
127     } else
128 #endif
129     {
130 	ret = wctomb(buf, inchar);
131 	if (ret <= 0) {
132 	    /* Ick. */
133 	    buf[0] = '?';
134 	    return 1;
135 	}
136     }
137     ptr = buf + ret - 1;
138     for (;;) {
139 	if (imeta(*ptr)) {
140 	    char *ptr2 = buf + ret - 1;
141 	    for (;;) {
142 		ptr2[1] = ptr2[0];
143 		if (ptr2 == ptr)
144 		    break;
145 		ptr2--;
146 	    }
147 	    *ptr = Meta;
148 	    ptr[1] ^= 32;
149 	    ret++;
150 	}
151 
152 	if (ptr == buf)
153 	    return ret;
154 	ptr--;
155     }
156 #else
157     if (imeta(inchar)) {
158 	buf[0] = Meta;
159 	buf[1] = inchar ^ 32;
160 	return 2;
161     } else {
162 	buf[0] = inchar;
163 	return 1;
164     }
165 #endif
166 }
167 
168 /*
169  * Input: a line in internal zle format, possibly using wide characters,
170  * possibly not, together with its length and the cursor position.
171  * The length must be accurate and includes all characters (no NULL
172  * termination is expected).  The input cursor position is only
173  * significant if outcs is non-NULL.
174  *
175  * Output: an ordinary NULL-terminated string, using multibyte characters
176  * instead of wide characters where appropriate and with the contents
177  * metafied.
178  *
179  * If outllp is non-NULL, assign the new length.  This is the conventional
180  * string length, without the NULL byte.
181  *
182  * If outcsp is non-NULL, assign the new character position.
183  * If outcsp is &zlemetacs, update the positions in the region_highlight
184  * array, too.  This is a bit of a hack.
185  *
186  * If useheap is 1, memory is returned from the heap, else is allocated
187  * for later freeing.
188  */
189 
190 /**/
191 mod_export char *
zlelineasstring(ZLE_STRING_T instr,int inll,int incs,int * outllp,int * outcsp,int useheap)192 zlelineasstring(ZLE_STRING_T instr, int inll, int incs, int *outllp,
193 		int *outcsp, int useheap)
194 {
195     int outcs, outll, sub;
196     struct region_highlight *rhp;
197 
198 #ifdef MULTIBYTE_SUPPORT
199     char *s;
200     int i, j;
201     size_t mb_len = 0;
202     mbstate_t mbs;
203 
204     s = zalloc(inll * MB_CUR_MAX + 1);
205 
206     outcs = 0;
207     memset(&mbs, 0, sizeof(mbs));
208     for (i=0; i < inll; i++) {
209 	if (incs == 0)
210 	    outcs = mb_len;
211 	incs--;
212 	if (region_highlights && outcsp == &zlemetacs) {
213 	    for (rhp = region_highlights + N_SPECIAL_HIGHLIGHTS;
214 		 rhp < region_highlights + n_region_highlights;
215 		 rhp++) {
216 		if (rhp->flags & ZRH_PREDISPLAY)
217 		    sub = predisplaylen;
218 		else
219 		    sub = 0;
220 		if (rhp->start - sub == 0)
221 		    rhp->start_meta = sub + mb_len;
222 		rhp->start--;
223 		if (rhp->end - sub == 0)
224 		    rhp->end_meta = sub + mb_len;
225 		rhp->end--;
226 	    }
227 	}
228 #ifdef __STDC_ISO_10646__
229 	if (ZSH_INVALID_WCHAR_TEST(instr[i])) {
230 	    s[mb_len++] = ZSH_INVALID_WCHAR_TO_CHAR(instr[i]);
231 	} else
232 #endif
233 	{
234 	    j = wcrtomb(s + mb_len, instr[i], &mbs);
235 	    if (j == -1) {
236 		/* invalid char */
237 		s[mb_len++] = ZWC('?');
238 		memset(&mbs, 0, sizeof(mbs));
239 	    } else {
240 		mb_len += j;
241 	    }
242 	}
243     }
244     if (incs == 0)
245 	outcs = mb_len;
246     if (region_highlights && outcsp == &zlemetacs) {
247 	for (rhp = region_highlights + N_SPECIAL_HIGHLIGHTS;
248 	     rhp < region_highlights + n_region_highlights;
249 	     rhp++) {
250 	    if (rhp->flags & ZRH_PREDISPLAY)
251 		sub = predisplaylen;
252 	    else
253 		sub = 0;
254 	    if (rhp->start - sub == 0)
255 		rhp->start_meta = sub + mb_len;
256 	    if (rhp->end - sub == 0)
257 		rhp->end_meta = sub + mb_len;
258 	}
259     }
260     s[mb_len] = '\0';
261 
262     outll = mb_len;
263 #else
264     outll = inll;
265     outcs = incs;
266     if (region_highlights && outcsp == &zlemetacs) {
267 	for (rhp = region_highlights + N_SPECIAL_HIGHLIGHTS;
268 	     rhp < region_highlights + n_region_highlights;
269 	     rhp++) {
270 	    rhp->start_meta = rhp->start;
271 	    rhp->end_meta = rhp->end;
272 	}
273     }
274 #endif
275 
276     /*
277      * *outcsp and *outllp are to be indexes into the final string,
278      * not character offsets, so we need to take account of any
279      * metafiable characters.
280      */
281     if (outcsp != NULL || outllp != NULL) {
282 #ifdef MULTIBYTE_SUPPORT
283 	char *strp = s;
284 #else
285 	char *strp = instr;
286 #endif
287 	char *stopcs = strp + outcs;
288 	char *stopll = strp + outll;
289 	char *startp = strp;
290 
291 	if (region_highlights && outcsp == &zlemetacs) {
292 	    for (rhp = region_highlights + N_SPECIAL_HIGHLIGHTS;
293 		 rhp < region_highlights + n_region_highlights;
294 		 rhp++) {
295 		/* Used as temporary storage */
296 		rhp->start = rhp->start_meta;
297 		rhp->end = rhp->end_meta;
298 	    }
299 	}
300 	while (strp < stopll) {
301 	    if (imeta(*strp)) {
302 		if (strp < stopcs)
303 		    outcs++;
304 		if (region_highlights && outcsp == &zlemetacs) {
305 		    for (rhp = region_highlights + N_SPECIAL_HIGHLIGHTS;
306 			 rhp < region_highlights + n_region_highlights;
307 			 rhp++) {
308 			if (rhp->flags & ZRH_PREDISPLAY)
309 			    sub = predisplaylen;
310 			else
311 			    sub = 0;
312 			if (strp < startp + rhp->start - sub) {
313 			    rhp->start_meta++;
314 			}
315 			if (strp < startp + rhp->end - sub) {
316 			    rhp->end_meta++;
317 			}
318 		    }
319 		}
320 		outll++;
321 	    }
322 	    strp++;
323 	}
324 	if (outcsp != NULL)
325 	    *outcsp = outcs;
326 	if (outllp != NULL)
327 	    *outllp = outll;
328     }
329 
330 #ifdef MULTIBYTE_SUPPORT
331     if (useheap) {
332 	char *ret = metafy(s, mb_len, META_HEAPDUP);
333 
334 	zfree(s, inll * MB_CUR_MAX + 1);
335 
336 	return ret;
337     }
338     return metafy(s, mb_len, META_REALLOC);
339 #else
340     return metafy(instr, inll, useheap ? META_HEAPDUP : META_DUP);
341 #endif
342 }
343 
344 
345 /*
346  * Input a NULL-terminated metafied string instr.
347  * Output a line in internal zle format, together with its length
348  * in the appropriate character units.  Note that outll may not be NULL.
349  *
350  * If outsz is non-NULL, the number of allocated characters in the
351  * string is written there.  For compatibility with use of the linesz
352  * variable (allocate size of zleline), at least two characters are
353  * allocated more than needed for immediate use.  (The extra characters
354  * may take a newline and a null at a later stage.)  These are not
355  * included in *outsz.
356  *
357  * If outcs is non-NULL, the character position in the original
358  * string incs (a standard string offset, i.e. incremented 2 for
359  * each metafied character) is converted into the corresponding
360  * character position in *outcs.
361  *
362  * If, further, outcs is &zlecs, we update the positions in the
363  * region_highlight array, too.  (This is a bit of a hack.)
364  *
365  * Note that instr is modified in place, hence should be copied
366  * first if necessary;
367  *
368  * Memory for the returned string is permanently allocated.  *outsz may
369  * be longer than the *outll returned.  Hence it should be freed with
370  * zfree(outstr, *outsz) or free(outstr), not zfree(outstr, *outll).
371  */
372 
373 /**/
374 mod_export ZLE_STRING_T
stringaszleline(char * instr,int incs,int * outll,int * outsz,int * outcs)375 stringaszleline(char *instr, int incs, int *outll, int *outsz, int *outcs)
376 {
377     ZLE_STRING_T outstr;
378     int ll, sz, sub;
379     struct region_highlight *rhp;
380 #ifdef MULTIBYTE_SUPPORT
381     mbstate_t mbs;
382 #endif
383 
384     if (outcs) {
385 	/*
386 	 * Take account of Meta characters in the input string
387 	 * before we unmetafy it.  This does not yet take account
388 	 * of multibyte characters.  If there are none, this
389 	 * is all the processing required to calculate outcs.
390 	 */
391 	char *inptr = instr, *cspos = instr + incs;
392 	if (region_highlights && outcs == &zlecs) {
393 	    for (rhp = region_highlights + N_SPECIAL_HIGHLIGHTS;
394 		 rhp < region_highlights + n_region_highlights;
395 		 rhp++) {
396 		rhp->start = rhp->start_meta;
397 		rhp->end = rhp->end_meta;
398 	    }
399 	}
400 	while (*inptr) {
401 	    if (*inptr == Meta) {
402 		if (inptr < cspos) {
403 		    incs--;
404 		}
405 		if (region_highlights && outcs == &zlecs) {
406 		    for (rhp = region_highlights + N_SPECIAL_HIGHLIGHTS;
407 			 rhp < region_highlights + n_region_highlights;
408 			 rhp++) {
409 			if (rhp->flags & ZRH_PREDISPLAY)
410 			    sub = predisplaylen;
411 			else
412 			    sub = 0;
413 			if (inptr - instr < rhp->start - sub) {
414 			    rhp->start_meta--;
415 			}
416 			if (inptr - instr < rhp->end - sub) {
417 			    rhp->end_meta--;
418 			}
419 		    }
420 		}
421 		inptr++;
422 	    }
423 	    inptr++;
424 	}
425     }
426     unmetafy(instr, &ll);
427 
428     /*
429      * ll is the maximum number of characters there can be in
430      * the output string; the closer to ASCII the string, the
431      * better the guess.  For the 2 see above.
432      */
433     sz = (ll + 2) * ZLE_CHAR_SIZE;
434     if (outsz)
435 	*outsz = ll;
436     outstr = (ZLE_STRING_T)zalloc(sz);
437 
438 #ifdef MULTIBYTE_SUPPORT
439     if (ll) {
440 	char *inptr = instr;
441 	wchar_t *outptr = outstr;
442 
443 	/* Reset shift state to input complete string */
444 	memset(&mbs, '\0', sizeof mbs);
445 
446 	while (ll > 0) {
447 	    size_t cnt = mbrtowc(outptr, inptr, ll, &mbs);
448 
449 #ifdef __STDC_ISO_10646__
450 	    if (cnt == MB_INCOMPLETE || cnt == MB_INVALID) {
451 		/* Use private encoding for invalid single byte */
452 		*outptr = ZSH_CHAR_TO_INVALID_WCHAR(*inptr);
453 		cnt = 1;
454 	    }
455 #else
456 	    /*
457 	     * At this point we don't handle either incomplete (-2) or
458 	     * invalid (-1) multibyte sequences.  Use the current length
459 	     * and return.
460 	     */
461 	    if (cnt == MB_INCOMPLETE || cnt == MB_INVALID)
462 		break;
463 #endif
464 
465 	    if (cnt == 0) {
466 		/* Converting '\0' returns 0, but a '\0' is a real
467 		 * character for us, so we should consume 1 byte
468 		 * (certainly true for Unicode and unlikely to be false
469 		 * in any non-pathological multibyte representation). */
470 		cnt = 1;
471 	    } else if (cnt > (size_t)ll) {
472 		/*
473 		 * Some multibyte implementations return the
474 		 * full length of a previous incomplete character
475 		 * instead of the remaining length.
476 		 * This is paranoia: it only applies if we start
477 		 * midway through a multibyte character, which
478 		 * presumably can't happen.
479 		 */
480 		cnt = ll;
481 	    }
482 
483 	    if (outcs) {
484 		int offs = inptr - instr;
485 		if (offs <= incs && incs < offs + (int)cnt)
486 		    *outcs = outptr - outstr;
487 		if (region_highlights && outcs == &zlecs) {
488 		    for (rhp = region_highlights + N_SPECIAL_HIGHLIGHTS;
489 			 rhp < region_highlights + n_region_highlights;
490 			 rhp++) {
491 			if (rhp->flags & ZRH_PREDISPLAY)
492 			    sub = predisplaylen;
493 			else
494 			    sub = 0;
495 			if (offs <= rhp->start_meta - sub &&
496 			    rhp->start_meta - sub < offs + (int)cnt) {
497 			    rhp->start = outptr - outstr + sub;
498 			}
499 			if (offs <= rhp->end_meta - sub &&
500 			    rhp->end_meta - sub < offs + (int)cnt) {
501 			    rhp->end = outptr - outstr + sub;
502 			}
503 		    }
504 		}
505 	    }
506 
507 	    inptr += cnt;
508 	    outptr++;
509 	    ll -= cnt;
510 	}
511 	if (outcs && inptr <= instr + incs)
512 	    *outcs = outptr - outstr;
513 	*outll = outptr - outstr;
514     } else {
515 	*outll = 0;
516 	if (outcs)
517 	    *outcs = 0;
518     }
519 #else
520     memcpy(outstr, instr, ll);
521     *outll = ll;
522     if (outcs)
523 	*outcs = incs;
524     if (region_highlights && outcs == &zlecs) {
525 	for (rhp = region_highlights + N_SPECIAL_HIGHLIGHTS;
526 	     rhp < region_highlights + n_region_highlights;
527 	     rhp++) {
528 	    rhp->start = rhp->start_meta;
529 	    rhp->end = rhp->end_meta;
530 	}
531     }
532 #endif
533 
534     return outstr;
535 }
536 
537 /*
538  * This function is called when we are playing very nasty tricks
539  * indeed: see bufferwords in hist.c.  Consequently we can make
540  * absolutely no assumption about the state whatsoever, except
541  * that it has one.
542  */
543 
544 /**/
545 mod_export char *
zlegetline(int * ll,int * cs)546 zlegetline(int *ll, int *cs)
547 {
548     if (zlemetaline != NULL) {
549 	*ll = zlemetall;
550 	*cs = zlemetacs;
551 	return ztrdup(zlemetaline);
552     }
553     if (zleline)
554 	return zlelineasstring(zleline, zlell, zlecs, ll, cs, 0);
555     *ll = *cs = 0;
556     return ztrdup("");
557 }
558 
559 
560 /* Forward reference */
561 struct zle_region;
562 
563 /* A non-special entry in region_highlight */
564 struct zle_region  {
565     struct zle_region *next;
566     /* Entries of region_highlight, as needed */
567     int atr;
568     int start;
569     int end;
570     int flags;
571 };
572 
573 /* Forward reference */
574 struct zle_position;
575 
576 /* A saved set of position information */
577 struct zle_position {
578     /* Link pointer */
579     struct zle_position *next;
580     /* Cursor position */
581     int cs;
582     /* Mark */
583     int mk;
584     /* Line length */
585     int ll;
586     struct zle_region *regions;
587 };
588 
589 /* LIFO stack of positions */
590 static struct zle_position *zle_positions;
591 
592 /*
593  * Save positions including cursor, end-of-line and
594  * (non-special) region highlighting.
595  *
596  * Must be matched by a subsequent zle_restore_positions().
597  */
598 
599 /**/
600 mod_export void
zle_save_positions(void)601 zle_save_positions(void)
602 {
603     struct region_highlight *rhp;
604     struct zle_position *newpos;
605     struct zle_region **newrhpp, *newrhp;
606 
607     newpos = (struct zle_position *)zalloc(sizeof(*newpos));
608 
609     newpos->mk = mark;
610     if (zlemetaline) {
611 	/* Use metafied information */
612 	newpos->cs = zlemetacs;
613 	newpos->ll = zlemetall;
614     } else {
615 	/* Use unmetafied information */
616 	newpos->cs = zlecs;
617 	newpos->ll = zlell;
618 
619     }
620 
621     newrhpp = &newpos->regions;
622     *newrhpp = NULL;
623     if (region_highlights) {
624 	for (rhp = region_highlights + N_SPECIAL_HIGHLIGHTS;
625 	     rhp < region_highlights + n_region_highlights;
626 	     rhp++) {
627 	    /*
628 	     * This is a FIFO stack, so we preserve the order
629 	     * of entries when we restore region_highlights.
630 	     */
631 	    newrhp = *newrhpp = (struct zle_region *)zalloc(sizeof(**newrhpp));
632 	    newrhp->next = NULL;
633 	    newrhp->atr = rhp->atr;
634 	    newrhp->flags = rhp->flags;
635 	    if (zlemetaline) {
636 		newrhp->start = rhp->start_meta;
637 		newrhp->end = rhp->end_meta;
638 	    } else {
639 		newrhp->start = rhp->start;
640 		newrhp->end = rhp->end;
641 	    }
642 	    newrhpp = &newrhp->next;
643 	}
644     }
645 
646     newpos->next = zle_positions;
647     zle_positions = newpos;
648 }
649 
650 /*
651  * Restore positions previously saved.
652  * Relies on zlemetaline being restored correctly beforehand,
653  * so that it can tell whether to use metafied positions or not.
654  */
655 
656 /**/
657 mod_export void
zle_restore_positions(void)658 zle_restore_positions(void)
659 {
660     struct zle_position *oldpos = zle_positions;
661     struct zle_region *oldrhp;
662     struct region_highlight *rhp;
663     int nreg;
664 
665     zle_positions = oldpos->next;
666 
667     mark = oldpos->mk;
668     if (zlemetaline) {
669 	/* Use metafied information */
670 	zlemetacs = oldpos->cs;
671 	zlemetall = oldpos->ll;
672     } else {
673 	/* Use unmetafied information */
674 	zlecs = oldpos->cs;
675 	zlell = oldpos->ll;
676     }
677 
678     if (oldpos->regions) {
679 	/* Count number of regions and see if the array needs resizing */
680 	for (nreg = 0, oldrhp = oldpos->regions;
681 	     oldrhp;
682 	     nreg++, oldrhp = oldrhp->next)
683 	    ;
684 	if (nreg + N_SPECIAL_HIGHLIGHTS != n_region_highlights) {
685 	    n_region_highlights = nreg + N_SPECIAL_HIGHLIGHTS;
686 	    region_highlights = (struct region_highlight *)
687 		zrealloc(region_highlights,
688 			 sizeof(struct region_highlight) * n_region_highlights);
689 	}
690 	oldrhp = oldpos->regions;
691 	rhp = region_highlights + N_SPECIAL_HIGHLIGHTS;
692 	while (oldrhp) {
693 	    struct zle_region *nextrhp = oldrhp->next;
694 
695 	    rhp->atr = oldrhp->atr;
696 	    rhp->flags = oldrhp->flags;
697 	    if (zlemetaline) {
698 		rhp->start_meta = oldrhp->start;
699 		rhp->end_meta = oldrhp->end;
700 	    } else {
701 		rhp->start = oldrhp->start;
702 		rhp->end = oldrhp->end;
703 	    }
704 
705 	    zfree(oldrhp, sizeof(*oldrhp));
706 	    oldrhp = nextrhp;
707 	    rhp++;
708 	}
709     } else if (region_highlights) {
710 	zfree(region_highlights, sizeof(struct region_highlight) *
711 	      n_region_highlights);
712 	region_highlights  = NULL;
713 	n_region_highlights = 0;
714     }
715 
716     zfree(oldpos, sizeof(*oldpos));
717 }
718 
719 /*
720  * Discard positions previously saved, the line has been updated.
721  */
722 
723 /**/
724 mod_export void
zle_free_positions(void)725 zle_free_positions(void)
726 {
727     struct zle_position *oldpos = zle_positions;
728     struct zle_region *oldrhp;
729 
730     zle_positions = oldpos->next;
731     oldrhp = oldpos->regions;
732     while (oldrhp) {
733 	struct zle_region *nextrhp = oldrhp->next;
734 	zfree(oldrhp, sizeof(*oldrhp));
735 	oldrhp = nextrhp;
736     }
737     zfree(oldpos, sizeof(*oldpos));
738 }
739 
740 /*
741  * Basic utility functions for adding to line or removing from line.
742  * At this level the counts supplied are raw character counts, so
743  * the calling code must be aware of combining characters where
744  * necessary, e.g. if we want to delete a + combing grave forward
745  * from the cursor, then shiftchars() gets the count 2 (not 1).
746  *
747  * This is necessary because these utility functions don't know about
748  * zlecs, and we need to count combined characters from there.
749  */
750 
751 /* insert space for ct chars at cursor position */
752 
753 /**/
754 mod_export void
spaceinline(int ct)755 spaceinline(int ct)
756 {
757     int i, sub;
758     struct region_highlight *rhp;
759 
760     if (zlemetaline) {
761 	sizeline(ct + zlemetall);
762 	for (i = zlemetall; --i >= zlemetacs;)
763 	    zlemetaline[i + ct] = zlemetaline[i];
764 	zlemetall += ct;
765 	zlemetaline[zlemetall] = '\0';
766 
767 	if (mark > zlemetacs)
768 	    mark += ct;
769 
770 	if (region_highlights) {
771 	    for (rhp = region_highlights + N_SPECIAL_HIGHLIGHTS;
772 		 rhp < region_highlights + n_region_highlights;
773 		 rhp++) {
774 		if (rhp->flags & ZRH_PREDISPLAY)
775 		    sub = predisplaylen;
776 		else
777 		    sub = 0;
778 		if (rhp->start_meta - sub >= zlemetacs) {
779 		    rhp->start_meta += ct;
780 		}
781 		if (rhp->end_meta - sub >= zlemetacs) {
782 		    rhp->end_meta += ct;
783 		}
784 	    }
785 	}
786     } else {
787 	sizeline(ct + zlell);
788 	for (i = zlell; --i >= zlecs;)
789 	    zleline[i + ct] = zleline[i];
790 	zlell += ct;
791 	zleline[zlell] = ZWC('\0');
792 
793 	if (mark > zlecs)
794 	    mark += ct;
795 	if (viinsbegin > zlecs)
796 	    viinsbegin = 0;
797 
798 	if (region_highlights) {
799 	    for (rhp = region_highlights + N_SPECIAL_HIGHLIGHTS;
800 		 rhp < region_highlights + n_region_highlights;
801 		 rhp++) {
802 		if (rhp->flags & ZRH_PREDISPLAY)
803 		    sub = predisplaylen;
804 		else
805 		    sub = 0;
806 		if (rhp->start - sub >= zlecs) {
807 		    rhp->start += ct;
808 		}
809 		if (rhp->end - sub >= zlecs) {
810 		    rhp->end += ct;
811 		}
812 	    }
813 	}
814     }
815     region_active = 0;
816 }
817 
818 /*
819  * Within the ZLE line, cut the "cnt" characters from position "to".
820  */
821 
822 /**/
823 void
shiftchars(int to,int cnt)824 shiftchars(int to, int cnt)
825 {
826     struct region_highlight *rhp;
827     int sub;
828 
829     if (mark >= to + cnt)
830 	mark -= cnt;
831     else if (mark > to)
832 	mark = to;
833 
834     if (zlemetaline) {
835 	/* before to is updated... */
836 	if (region_highlights) {
837 	    for (rhp = region_highlights + N_SPECIAL_HIGHLIGHTS;
838 		 rhp < region_highlights + n_region_highlights;
839 		 rhp++) {
840 		if (rhp->flags & ZRH_PREDISPLAY)
841 		    sub = predisplaylen;
842 		else
843 		    sub = 0;
844 		if (rhp->start_meta - sub > to) {
845 		    if (rhp->start_meta - sub > to + cnt)
846 			rhp->start_meta -= cnt;
847 		    else
848 			rhp->start_meta = to;
849 		}
850 		if (rhp->end_meta - sub > to) {
851 		    if (rhp->end_meta - sub > to + cnt)
852 			rhp->end_meta -= cnt;
853 		    else
854 			rhp->end_meta = to;
855 		}
856 	    }
857 	}
858 
859 	while (to + cnt < zlemetall) {
860 	    zlemetaline[to] = zlemetaline[to + cnt];
861 	    to++;
862 	}
863 	zlemetaline[zlemetall = to] = '\0';
864     } else {
865 	/* before to is updated... */
866 	if (region_highlights) {
867 	    for (rhp = region_highlights + N_SPECIAL_HIGHLIGHTS;
868 		 rhp < region_highlights + n_region_highlights;
869 		 rhp++) {
870 		if (rhp->flags & ZRH_PREDISPLAY)
871 		    sub = predisplaylen;
872 		else
873 		    sub = 0;
874 		if (rhp->start - sub > to) {
875 		    if (rhp->start - sub > to + cnt)
876 			rhp->start -= cnt;
877 		    else
878 			rhp->start = to;
879 		}
880 		if (rhp->end - sub > to) {
881 		    if (rhp->end - sub > to + cnt)
882 			rhp->end -= cnt;
883 		    else
884 			rhp->end = to;
885 		}
886 	    }
887 	}
888 
889 	while (to + cnt < zlell) {
890 	    zleline[to] = zleline[to + cnt];
891 	    to++;
892 	}
893 	zleline[zlell = to] = ZWC('\0');
894     }
895     region_active = 0;
896 }
897 
898 /*
899  * Put the ct characters starting at zleline + i into the
900  * cutbuffer, circling the kill ring if necessary (it's
901  * not if we're dealing with vi buffers, which is detected
902  * internally).  The text is not removed from zleline.
903  *
904  * dir indicates how the text is to be added to the cutbuffer,
905  * if the cutbuffer wasn't zeroed (this depends on the last
906  * command being a kill).  If dir is 1, the new text goes
907  * to the front of the cut buffer.  If dir is -1, the cutbuffer
908  * is completely overwritten.
909  */
910 
911 /**/
912 void
cut(int i,int ct,int flags)913 cut(int i, int ct, int flags)
914 {
915   cuttext(zleline + i, ct, flags);
916 }
917 
918 /*
919  * As cut, but explicitly supply the text together with its length.
920  */
921 
922 /**/
923 void
cuttext(ZLE_STRING_T line,int ct,int flags)924 cuttext(ZLE_STRING_T line, int ct, int flags)
925 {
926     if (!(ct || vilinerange) ||  zmod.flags & MOD_NULL)
927 	return;
928 
929     UNMETACHECK();
930     if (zmod.flags & MOD_VIBUF) {
931 	struct cutbuffer *b = &vibuf[zmod.vibuf];
932 
933 	if (!(zmod.flags & MOD_VIAPP) || !b->buf) {
934 	    free(b->buf);
935 	    b->buf = (ZLE_STRING_T)zalloc(ct * ZLE_CHAR_SIZE);
936 	    ZS_memcpy(b->buf, line, ct);
937 	    b->len = ct;
938 	    b->flags = vilinerange ? CUTBUFFER_LINE : 0;
939 	} else {
940 	    int len = b->len;
941 
942 	    if(vilinerange)
943 		b->flags |= CUTBUFFER_LINE;
944 	    b->buf = (ZLE_STRING_T)
945 		realloc((char *)b->buf,
946 			(ct + len + !!(b->flags & CUTBUFFER_LINE))
947 			* ZLE_CHAR_SIZE);
948 	    if (b->flags & CUTBUFFER_LINE)
949 		b->buf[len++] = ZWC('\n');
950 	    ZS_memcpy(b->buf + len, line, ct);
951 	    b->len = len + ct;
952 	}
953     } else if (flags & CUT_YANK) {
954 	/* Save in "0 */
955 	free(vibuf[26].buf);
956 	vibuf[26].buf = (ZLE_STRING_T)zalloc(ct * ZLE_CHAR_SIZE);
957 	ZS_memcpy(vibuf[26].buf, line, ct);
958 	vibuf[26].len = ct;
959 	vibuf[26].flags = vilinerange ? CUTBUFFER_LINE : 0;
960     } else {
961 	/* Save in "1, shifting "1-"8 along to "2-"9 */
962 	int n;
963 	free(vibuf[35].buf);
964 	for(n=35; n>27; n--)
965 	    vibuf[n] = vibuf[n-1];
966 	vibuf[27].buf = (ZLE_STRING_T)zalloc(ct * ZLE_CHAR_SIZE);
967 	ZS_memcpy(vibuf[27].buf, line, ct);
968 	vibuf[27].len = ct;
969 	vibuf[27].flags = vilinerange ? CUTBUFFER_LINE : 0;
970     }
971     if (!cutbuf.buf) {
972 	cutbuf.buf = (ZLE_STRING_T)zalloc(ZLE_CHAR_SIZE);
973 	cutbuf.buf[0] = ZWC('\0');
974 	cutbuf.len = cutbuf.flags = 0;
975     } else if (!(lastcmd & ZLE_KILL) || (flags & CUT_REPLACE)) {
976 	Cutbuffer kptr;
977 	if (!kring) {
978 	    kringsize = KRINGCTDEF;
979 	    kring = (Cutbuffer)zshcalloc(kringsize * sizeof(struct cutbuffer));
980 	} else
981 	    kringnum = (kringnum + 1) % kringsize;
982 	kptr = kring + kringnum;
983 	if (kptr->buf)
984 	    free(kptr->buf);
985 	*kptr = cutbuf;
986 	cutbuf.buf = (ZLE_STRING_T)zalloc(ZLE_CHAR_SIZE);
987 	cutbuf.buf[0] = ZWC('\0');
988 	cutbuf.len = cutbuf.flags = 0;
989     }
990     if (flags & (CUT_FRONT|CUT_REPLACE)) {
991 	ZLE_STRING_T s = (ZLE_STRING_T)zalloc((cutbuf.len + ct)*ZLE_CHAR_SIZE);
992 
993 	ZS_memcpy(s, line, ct);
994 	ZS_memcpy(s + ct, cutbuf.buf, cutbuf.len);
995 	free(cutbuf.buf);
996 	cutbuf.buf = s;
997 	cutbuf.len += ct;
998     } else {
999 	/* don't alloc 0 bytes; length 0 occurs for blank lines in vi mode */
1000 	cutbuf.buf = realloc((char *)cutbuf.buf,
1001 			     (cutbuf.len + (ct ? ct : 1)) * ZLE_CHAR_SIZE);
1002 	ZS_memcpy(cutbuf.buf + cutbuf.len, line, ct);
1003 	cutbuf.len += ct;
1004     }
1005     if(vilinerange)
1006 	cutbuf.flags |= CUTBUFFER_LINE;
1007     else
1008 	cutbuf.flags &= ~CUTBUFFER_LINE;
1009 }
1010 
1011 /*
1012  * Now we're back in the world of zlecs where we need to keep
1013  * track of whether we're on a combining character.
1014  */
1015 
1016 /**/
1017 mod_export void
backkill(int ct,int flags)1018 backkill(int ct, int flags)
1019 {
1020     UNMETACHECK();
1021     if (flags & CUT_RAW) {
1022 	zlecs -= ct;
1023     } else {
1024 	int origcs = zlecs;
1025 	while (ct--)
1026 	    DECCS();
1027 	ct = origcs - zlecs;
1028     }
1029 
1030     cut(zlecs, ct, flags);
1031     shiftchars(zlecs, ct);
1032     CCRIGHT();
1033 }
1034 
1035 /**/
1036 mod_export void
forekill(int ct,int flags)1037 forekill(int ct, int flags)
1038 {
1039     int i = zlecs;
1040 
1041     UNMETACHECK();
1042     if (!(flags & CUT_RAW)) {
1043 	int n = ct;
1044 	while (n--)
1045 	    INCCS();
1046 	ct = zlecs - i;
1047 	zlecs = i;
1048     }
1049 
1050     cut(i, ct, flags);
1051     shiftchars(i, ct);
1052     CCRIGHT();
1053 }
1054 
1055 /**/
1056 mod_export void
backdel(int ct,int flags)1057 backdel(int ct, int flags)
1058 {
1059     if (flags & CUT_RAW) {
1060 	if (zlemetaline != NULL) {
1061 	    shiftchars(zlemetacs -= ct, ct);
1062 	} else {
1063 	    shiftchars(zlecs -= ct, ct);
1064 	    CCRIGHT();
1065 	}
1066     } else {
1067 	int n = ct, origcs = zlecs;
1068 	DPUTS(zlemetaline != NULL, "backdel needs CUT_RAW when metafied");
1069 	while (n--)
1070 	    DECCS();
1071 	shiftchars(zlecs, origcs - zlecs);
1072 	CCRIGHT();
1073     }
1074 }
1075 
1076 /**/
1077 mod_export void
foredel(int ct,int flags)1078 foredel(int ct, int flags)
1079 {
1080     if (flags & CUT_RAW) {
1081 	if (zlemetaline != NULL) {
1082 	    shiftchars(zlemetacs, ct);
1083 	} else if (flags & CUT_RAW) {
1084 	    shiftchars(zlecs, ct);
1085 	    CCRIGHT();
1086 	}
1087     } else {
1088 	int origcs = zlecs;
1089 	int n = ct;
1090 	DPUTS(zlemetaline != NULL, "foredel needs CUT_RAW when metafied");
1091 	while (n--)
1092 	    INCCS();
1093 	ct = zlecs - origcs;
1094 	zlecs = origcs;
1095 	shiftchars(zlecs, ct);
1096 	CCRIGHT();
1097     }
1098 }
1099 
1100 /**/
1101 void
setline(char * s,int flags)1102 setline(char *s, int flags)
1103 {
1104     char *scp;
1105 
1106     UNMETACHECK();
1107     if (flags & ZSL_COPY)
1108 	scp = ztrdup(s);
1109     else
1110 	scp = s;
1111     /*
1112      * TBD: we could make this more efficient by passing the existing
1113      * allocated line to stringaszleline.
1114      */
1115     free(zleline);
1116 
1117     viinsbegin = 0;
1118     zleline = stringaszleline(scp, 0, &zlell, &linesz, NULL);
1119 
1120     if ((flags & ZSL_TOEND) && (zlecs = zlell) && invicmdmode())
1121 	DECCS();
1122     else if (zlecs > zlell)
1123 	zlecs = zlell;
1124     CCRIGHT();
1125     if (flags & ZSL_COPY)
1126 	free(scp);
1127 }
1128 
1129 /**/
1130 int
findbol(void)1131 findbol(void)
1132 {
1133     int x = zlecs;
1134 
1135     while (x > 0 && zleline[x - 1] != ZWC('\n'))
1136 	x--;
1137     return x;
1138 }
1139 
1140 /**/
1141 int
findeol(void)1142 findeol(void)
1143 {
1144     int x = zlecs;
1145 
1146     while (x != zlell && zleline[x] != ZWC('\n'))
1147 	x++;
1148     return x;
1149 }
1150 
1151 /**/
1152 void
findline(int * a,int * b)1153 findline(int *a, int *b)
1154 {
1155     *a = findbol();
1156     *b = findeol();
1157 }
1158 
1159 /*
1160  * Query the user, and return 1 for yes, 0 for no.  The question is assumed to
1161  * have been printed already, and the cursor is left immediately after the
1162  * response echoed.  (Might cause a problem if this takes it onto the next
1163  * line.)  <Tab> is interpreted as 'y'; any other control character is
1164  * interpreted as 'n'.  If there are any characters in the buffer, this is
1165  * taken as a negative response, and no characters are read.  Case is folded.
1166  */
1167 
1168 /**/
1169 mod_export int
getzlequery(void)1170 getzlequery(void)
1171 {
1172     ZLE_INT_T c;
1173 #ifdef FIONREAD
1174     int val;
1175 
1176     /* check for typeahead, which is treated as a negative response */
1177     ioctl(SHTTY, FIONREAD, (char *)&val);
1178     if (val) {
1179 	putc('n', shout);
1180 	return 0;
1181     }
1182 #endif
1183 
1184     /* get a character from the tty and interpret it */
1185     c = getfullchar(0);
1186     /*
1187      * We'll interpret an interruption here as only interrupting the
1188      * query, not the line editor.
1189      */
1190     errflag &= ~ERRFLAG_INT;
1191     if (c == ZWC('\t'))
1192 	c = ZWC('y');
1193     else if (ZC_icntrl(c) || c == ZLEEOF)
1194 	c = ZWC('n');
1195     else
1196 	c = ZC_tolower(c);
1197     /* echo response and return */
1198     if (c != ZWC('\n')) {
1199 	REFRESH_ELEMENT re;
1200 	re.chr = c;
1201 	re.atr = 0;
1202 	zwcputc(&re, NULL);
1203     }
1204     return c == ZWC('y');
1205 }
1206 
1207 /* Format a string, keybinding style. */
1208 
1209 /**/
1210 char *
bindztrdup(char * str)1211 bindztrdup(char *str)
1212 {
1213     int c, len = 1;
1214     char *buf, *ptr, *ret;
1215 
1216     for(ptr = str; *ptr; ptr++) {
1217 	c = *ptr == Meta ? STOUC(*++ptr) ^ 32 : STOUC(*ptr);
1218 	if(c & 0x80) {
1219 	    len += 3;
1220 	    c &= 0x7f;
1221 	}
1222 	if(c < 32 || c == 0x7f) {
1223 	    len++;
1224 	    c ^= 64;
1225 	}
1226 	len += c == '\\' || c == '^';
1227 	len++;
1228     }
1229     ptr = buf = zalloc(len);
1230     for(; *str; str++) {
1231 	c = *str == Meta ? STOUC(*++str) ^ 32 : STOUC(*str);
1232 	if(c & 0x80) {
1233 	    *ptr++ = '\\';
1234 	    *ptr++ = 'M';
1235 	    *ptr++ = '-';
1236 	    c &= 0x7f;
1237 	}
1238 	if(c < 32 || c == 0x7f) {
1239 	    *ptr++ = '^';
1240 	    c ^= 64;
1241 	}
1242 	if(c == '\\' || c == '^')
1243 	    *ptr++ = '\\';
1244 	*ptr++ = c;
1245     }
1246     *ptr = 0;
1247     ret = dquotedztrdup(buf);
1248     zsfree(buf);
1249     return ret;
1250 }
1251 
1252 /* Display a metafied string, keybinding-style. */
1253 
1254 /**/
1255 int
printbind(char * str,FILE * stream)1256 printbind(char *str, FILE *stream)
1257 {
1258     char *b = bindztrdup(str);
1259     int ret = zputs(b, stream);
1260 
1261     zsfree(b);
1262     return ret;
1263 }
1264 
1265 /*
1266  * Display a message where the completion list normally goes.
1267  * The message must be metafied.
1268  *
1269  * TODO: there's some advantage in using a ZLE_STRING_T array here,
1270  * together with improvements in other places, but messages don't
1271  * need to be particularly efficient.
1272  */
1273 
1274 /**/
1275 mod_export void
showmsg(char const * msg)1276 showmsg(char const *msg)
1277 {
1278     char const *p;
1279     int up = 0, cc = 0;
1280     ZLE_CHAR_T c;
1281 #ifdef MULTIBYTE_SUPPORT
1282     char *umsg;
1283     int ulen, eol = 0;
1284     size_t width;
1285     mbstate_t mbs;
1286 #endif
1287 
1288     trashzle();
1289     clearflag = isset(USEZLE) && !termflags && isset(ALWAYSLASTPROMPT);
1290 
1291 #ifdef MULTIBYTE_SUPPORT
1292     umsg = ztrdup(msg);
1293     p = unmetafy(umsg, &ulen);
1294     memset(&mbs, 0, sizeof mbs);
1295 
1296     mb_charinit();
1297     while (ulen > 0) {
1298 	char const *n;
1299 	if (*p == '\n') {
1300 	    ulen--;
1301 	    p++;
1302 
1303 	    putc('\n', shout);
1304 	    up += 1 + cc / zterm_columns;
1305 	    cc = 0;
1306 	} else {
1307 	    /*
1308 	     * Extract the next wide character from the multibyte string.
1309 	     */
1310 	    size_t cnt = eol ? MB_INVALID : mbrtowc(&c, p, ulen, &mbs);
1311 
1312 	    switch (cnt) {
1313 	    case MB_INCOMPLETE:
1314 		eol = 1;
1315 		/* FALL THROUGH */
1316 	    case MB_INVALID:
1317 		/*
1318 		 * This really shouldn't be happening here, but...
1319 		 * Treat it as a single byte character; it may get
1320 		 * prettified.
1321 		 */
1322 		memset(&mbs, 0, sizeof mbs);
1323 		n = nicechar(*p);
1324 		cnt = 1;
1325 		width = strlen(n);
1326 		break;
1327 	    case 0:
1328 		cnt = 1;
1329 		/* FALL THROUGH */
1330 	    default:
1331 		/*
1332 		 * Paranoia: only needed if we start in the middle
1333 		 * of a multibyte string and only in some implementations.
1334 		 */
1335 		if (cnt > (size_t)ulen)
1336 		    cnt = ulen;
1337 		n = wcs_nicechar(c, &width, NULL);
1338 		break;
1339 	    }
1340 	    ulen -= cnt;
1341 	    p += cnt;
1342 
1343 	    zputs(n, shout);
1344 	    cc += width;
1345 	}
1346     }
1347 
1348     free(umsg);
1349 #else
1350     for(p = msg; (c = *p); p++) {
1351 	if(c == Meta)
1352 	    c = *++p ^ 32;
1353 	if(c == '\n') {
1354 	    putc('\n', shout);
1355 	    up += 1 + cc / zterm_columns;
1356 	    cc = 0;
1357 	} else {
1358 	    char const *n = nicechar(c);
1359 	    zputs(n, shout);
1360 	    cc += strlen(n);
1361 	}
1362     }
1363 #endif
1364     up += cc / zterm_columns;
1365 
1366     if (clearflag) {
1367 	putc('\r', shout);
1368 	tcmultout(TCUP, TCMULTUP, up + nlnct);
1369     } else
1370 	putc('\n', shout);
1371     showinglist = 0;
1372 }
1373 
1374 /* handle the error flag */
1375 
1376 /**/
1377 int
handlefeep(UNUSED (char ** args))1378 handlefeep(UNUSED(char **args))
1379 {
1380     zbeep();
1381     return 0;
1382 }
1383 
1384 /* user control of auto-suffixes -- see iwidgets.list */
1385 
1386 /**/
1387 int
handlesuffix(UNUSED (char ** args))1388 handlesuffix(UNUSED(char **args))
1389 {
1390   return 0;
1391 }
1392 
1393 /***************/
1394 /* undo system */
1395 /***************/
1396 
1397 /* head of the undo list, and the current position */
1398 
1399 /**/
1400 struct change *curchange;
1401 
1402 static struct change *changes;
1403 
1404 /* list of pending changes, not yet in the undo system */
1405 
1406 static struct change *nextchanges, *endnextchanges;
1407 
1408 /* incremented to provide a unique change number */
1409 
1410 /**/
1411 zlong undo_changeno;
1412 
1413 /* If positive, don't undo beyond this point */
1414 
1415 static zlong undo_limitno;
1416 
1417 /**/
1418 void
initundo(void)1419 initundo(void)
1420 {
1421     nextchanges = NULL;
1422     changes = curchange = zalloc(sizeof(*curchange));
1423     curchange->prev = curchange->next = NULL;
1424     curchange->del = curchange->ins = NULL;
1425     curchange->dell = curchange->insl = 0;
1426     curchange->changeno = undo_changeno = undo_limitno = 0;
1427     lastline = zalloc((lastlinesz = linesz) * ZLE_CHAR_SIZE);
1428     ZS_memcpy(lastline, zleline, (lastll = zlell));
1429     lastcs = zlecs;
1430 }
1431 
1432 /**/
1433 void
freeundo(void)1434 freeundo(void)
1435 {
1436     freechanges(changes);
1437     freechanges(nextchanges);
1438     zfree(lastline, lastlinesz);
1439     lastline = NULL;
1440     lastlinesz = 0;
1441 }
1442 
1443 /**/
1444 static void
freechanges(struct change * p)1445 freechanges(struct change *p)
1446 {
1447     struct change *n;
1448 
1449     for(; p; p = n) {
1450 	n = p->next;
1451 	free(p->del);
1452 	free(p->ins);
1453 	zfree(p, sizeof(*p));
1454     }
1455 }
1456 
1457 /* register pending changes in the undo system */
1458 
1459 /**/
1460 mod_export void
handleundo(void)1461 handleundo(void)
1462 {
1463     int remetafy;
1464 
1465     /*
1466      * Yuk: we call this from within the completion system,
1467      * so we need to convert back to the form which can be
1468      * copied into undo entries.
1469      */
1470     if (zlemetaline != NULL) {
1471 	unmetafy_line();
1472 	remetafy = 1;
1473     } else
1474 	remetafy = 0;
1475 
1476     mkundoent();
1477     if(nextchanges) {
1478 	setlastline();
1479 	if(curchange->next) {
1480 	    freechanges(curchange->next);
1481 	    curchange->next = NULL;
1482 	    free(curchange->del);
1483 	    free(curchange->ins);
1484 	    curchange->del = curchange->ins = NULL;
1485 	    curchange->dell = curchange->insl = 0;
1486 	}
1487 	nextchanges->prev = curchange->prev;
1488 	if(curchange->prev)
1489 	    curchange->prev->next = nextchanges;
1490 	else
1491 	    changes = nextchanges;
1492 	curchange->prev = endnextchanges;
1493 	endnextchanges->next = curchange;
1494 	nextchanges = endnextchanges = NULL;
1495     }
1496 
1497     if (remetafy)
1498 	metafy_line();
1499 }
1500 
1501 /* add an entry to the undo system, if anything has changed */
1502 
1503 /**/
1504 void
mkundoent(void)1505 mkundoent(void)
1506 {
1507     int pre, suf;
1508     int sh = zlell < lastll ? zlell : lastll;
1509     struct change *ch;
1510 
1511     UNMETACHECK();
1512     if(lastll == zlell && !ZS_memcmp(lastline, zleline, zlell)) {
1513 	lastcs = zlecs;
1514 	return;
1515     }
1516     for(pre = 0; pre < sh && zleline[pre] == lastline[pre]; )
1517 	pre++;
1518     for(suf = 0; suf < sh - pre &&
1519 	zleline[zlell - 1 - suf] == lastline[lastll - 1 - suf]; )
1520 	suf++;
1521     ch = zalloc(sizeof(*ch));
1522     ch->next = NULL;
1523     ch->hist = histline;
1524     ch->off = pre;
1525     ch->old_cs = lastcs;
1526     ch->new_cs = zlecs;
1527     if(suf + pre == lastll) {
1528 	ch->del = NULL;
1529 	ch->dell = 0;
1530     } else {
1531 	ch->dell = lastll - pre - suf;
1532 	ch->del = (ZLE_STRING_T)zalloc(ch->dell * ZLE_CHAR_SIZE);
1533 	ZS_memcpy(ch->del, lastline + pre, ch->dell);
1534     }
1535     if(suf + pre == zlell) {
1536 	ch->ins = NULL;
1537 	ch->insl = 0;
1538     } else {
1539 	ch->insl = zlell - pre - suf;
1540 	ch->ins = (ZLE_STRING_T)zalloc(ch->insl * ZLE_CHAR_SIZE);
1541 	ZS_memcpy(ch->ins, zleline + pre, ch->insl);
1542     }
1543     if(nextchanges) {
1544 	ch->flags = CH_PREV;
1545 	ch->prev = endnextchanges;
1546 	endnextchanges->flags |= CH_NEXT;
1547 	endnextchanges->next = ch;
1548     } else {
1549 	nextchanges = ch;
1550 	ch->flags = 0;
1551 	ch->prev = NULL;
1552     }
1553     ch->changeno = ++undo_changeno;
1554     endnextchanges = ch;
1555 }
1556 
1557 /* set lastline to match line */
1558 
1559 /**/
1560 void
setlastline(void)1561 setlastline(void)
1562 {
1563     UNMETACHECK();
1564     if(lastlinesz != linesz)
1565 	lastline = realloc(lastline, (lastlinesz = linesz) * ZLE_CHAR_SIZE);
1566     ZS_memcpy(lastline, zleline, (lastll = zlell));
1567     lastcs = zlecs;
1568 }
1569 
1570 /* move backwards through the change list */
1571 
1572 /**/
1573 int
undo(char ** args)1574 undo(char **args)
1575 {
1576     zlong last_change;
1577 
1578     if (*args)
1579 	last_change = zstrtol(*args, NULL, 0);
1580     else
1581 	last_change = (zlong)-1;
1582 
1583     handleundo();
1584     do {
1585 	struct change *prev = curchange->prev;
1586 	if(!prev)
1587 	    return 1;
1588 	if (prev->changeno <= last_change)
1589 	    break;
1590 	if (prev->changeno <= undo_limitno && !*args)
1591 	    return 1;
1592 	if (!unapplychange(prev)) {
1593 	    if (last_change >= 0) {
1594 		unapplychange(prev);
1595 		curchange = prev;
1596 	    }
1597 	} else {
1598 	    curchange = prev;
1599 	}
1600     } while (last_change >= (zlong)0 || (curchange->flags & CH_PREV));
1601     setlastline();
1602     return 0;
1603 }
1604 
1605 /**/
1606 static int
unapplychange(struct change * ch)1607 unapplychange(struct change *ch)
1608 {
1609     if(ch->hist != histline) {
1610 	Histent he = quietgethist(ch->hist);
1611 	DPUTS(he == NULL, "quietgethist(ch->hist) returned NULL");
1612 	if(he == NULL)
1613 	    return 1;
1614 	zle_setline(he);
1615 	zlecs = ch->new_cs;
1616 	return 0;
1617     }
1618     zlecs = ch->off;
1619     if(ch->ins)
1620 	foredel(ch->insl, CUT_RAW);
1621     if(ch->del) {
1622 	spaceinline(ch->dell);
1623 	ZS_memcpy(zleline + zlecs, ch->del, ch->dell);
1624 	zlecs += ch->dell;
1625     }
1626     zlecs = ch->old_cs;
1627     return 1;
1628 }
1629 
1630 /* move forwards through the change list */
1631 
1632 /**/
1633 int
redo(UNUSED (char ** args))1634 redo(UNUSED(char **args))
1635 {
1636     handleundo();
1637     do {
1638 	if(!curchange->next)
1639 	    return 1;
1640 	if (applychange(curchange))
1641 	    curchange = curchange->next;
1642 	else
1643 	    break;
1644     } while(curchange->prev->flags & CH_NEXT);
1645     setlastline();
1646     return 0;
1647 }
1648 
1649 /**/
1650 static int
applychange(struct change * ch)1651 applychange(struct change *ch)
1652 {
1653     if(ch->hist != histline) {
1654 	Histent he = quietgethist(ch->hist);
1655 	DPUTS(he == NULL, "quietgethist(ch->hist) returned NULL");
1656 	if(he == NULL)
1657 	    return 1;
1658 	zle_setline(he);
1659 	zlecs = ch->old_cs;
1660 	return 0;
1661     }
1662     zlecs = ch->off;
1663     if(ch->del)
1664 	foredel(ch->dell, CUT_RAW);
1665     if(ch->ins) {
1666 	spaceinline(ch->insl);
1667 	ZS_memcpy(zleline + zlecs, ch->ins, ch->insl);
1668 	zlecs += ch->insl;
1669     }
1670     zlecs = ch->new_cs;
1671     return 1;
1672 }
1673 
1674 /* vi undo: toggle between the end of the undo list and the preceding point */
1675 
1676 /**/
1677 int
viundochange(char ** args)1678 viundochange(char **args)
1679 {
1680     handleundo();
1681     if(curchange->next) {
1682 	do {
1683 	    applychange(curchange);
1684 	    curchange = curchange->next;
1685 	} while(curchange->next);
1686 	setlastline();
1687 	return 0;
1688     } else
1689 	return undo(args);
1690 }
1691 
1692 /**/
1693 int
splitundo(UNUSED (char ** args))1694 splitundo(UNUSED(char **args))
1695 {
1696     if (vistartchange >= 0) {
1697 	mergeundo();
1698 	vistartchange = undo_changeno;
1699     }
1700     handleundo();
1701     return 0;
1702 }
1703 
1704 /**/
1705 void
mergeundo(void)1706 mergeundo(void)
1707 {
1708     struct change *current;
1709     for (current = curchange->prev;
1710 	    current && current->prev && current->changeno > vistartchange+1;
1711 	    current = current->prev) {
1712 	current->flags |= CH_PREV;
1713 	current->prev->flags |= CH_NEXT;
1714     }
1715     vistartchange = -1;
1716 }
1717 
1718 /*
1719  * Call a ZLE hook: a user-defined widget called at a specific point
1720  * within the line editor.
1721  *
1722  * A single argument arg is passed to the function (in addition to the
1723  * function name).  It may be NULL.
1724  */
1725 
1726 /**/
1727 void
zlecallhook(char * name,char * arg)1728 zlecallhook(char *name, char *arg)
1729 {
1730     Thingy thingy = rthingy_nocreate(name);
1731     int saverrflag, savretflag;
1732     char *args[2];
1733 
1734     if (!thingy)
1735 	return;
1736 
1737     /* If anything here needs changing, see also redrawhook() */
1738 
1739     saverrflag = errflag;
1740     savretflag = retflag;
1741 
1742     args[0] = arg;
1743     args[1] = NULL;
1744     execzlefunc(thingy, args, 1, 0);
1745     unrefthingy(thingy);
1746 
1747     /* Retain any user interrupt error status */
1748     errflag = saverrflag | (errflag & ERRFLAG_INT);
1749     retflag = savretflag;
1750 }
1751 
1752 /*
1753  * Return the number corresponding to the last change made.
1754  */
1755 
1756 /**/
1757 zlong
get_undo_current_change(UNUSED (Param pm))1758 get_undo_current_change(UNUSED(Param pm))
1759 {
1760     int remetafy;
1761 
1762     /*
1763      * Yuk: we call this from within the completion system,
1764      * so we need to convert back to the form which can be
1765      * copied into undo entries.
1766      */
1767     if (zlemetaline != NULL) {
1768 	unmetafy_line();
1769 	remetafy = 1;
1770     } else
1771 	remetafy = 0;
1772 
1773     /* add entry for any pending changes */
1774     mkundoent();
1775     setlastline();
1776 
1777     if (remetafy)
1778 	metafy_line();
1779 
1780     return undo_changeno;
1781 }
1782 
1783 /**/
1784 zlong
get_undo_limit_change(UNUSED (Param pm))1785 get_undo_limit_change(UNUSED(Param pm))
1786 {
1787     return undo_limitno;
1788 }
1789 
1790 /**/
1791 void
set_undo_limit_change(UNUSED (Param pm),zlong value)1792 set_undo_limit_change(UNUSED(Param pm), zlong value)
1793 {
1794     undo_limitno = value;
1795 }
1796