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