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	    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/**/
191mod_export char *
192zlelineasstring(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/**/
374mod_export ZLE_STRING_T
375stringaszleline(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/**/
545mod_export char *
546zlegetline(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 */
561struct zle_region;
562
563/* A non-special entry in region_highlight */
564struct 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 */
574struct zle_position;
575
576/* A saved set of position information */
577struct 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 */
590struct 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/**/
600mod_export void
601zle_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/**/
657mod_export void
658zle_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    /* Count number of regions and see if the array needs resizing */
679    for (nreg = 0, oldrhp = oldpos->regions;
680	 oldrhp;
681	 nreg++, oldrhp = oldrhp->next)
682	;
683    if (nreg + N_SPECIAL_HIGHLIGHTS != n_region_highlights) {
684	n_region_highlights = nreg + N_SPECIAL_HIGHLIGHTS;
685	region_highlights = (struct region_highlight *)
686	    zrealloc(region_highlights,
687		     sizeof(struct region_highlight) * n_region_highlights);
688    }
689    oldrhp = oldpos->regions;
690    rhp = region_highlights + N_SPECIAL_HIGHLIGHTS;
691    while (oldrhp) {
692	struct zle_region *nextrhp = oldrhp->next;
693
694	rhp->atr = oldrhp->atr;
695	rhp->flags = oldrhp->flags;
696	if (zlemetaline) {
697	    rhp->start_meta = oldrhp->start;
698	    rhp->end_meta = oldrhp->end;
699	} else {
700	    rhp->start = oldrhp->start;
701	    rhp->end = oldrhp->end;
702	}
703
704	zfree(oldrhp, sizeof(*oldrhp));
705	oldrhp = nextrhp;
706	rhp++;
707    }
708
709    zfree(oldpos, sizeof(*oldpos));
710}
711
712/*
713 * Basic utility functions for adding to line or removing from line.
714 * At this level the counts supplied are raw character counts, so
715 * the calling code must be aware of combining characters where
716 * necessary, e.g. if we want to delete a + combing grave forward
717 * from the cursor, then shiftchars() gets the count 2 (not 1).
718 *
719 * This is necessary because these utility functions don't know about
720 * zlecs, and we need to count combined characters from there.
721 */
722
723/* insert space for ct chars at cursor position */
724
725/**/
726mod_export void
727spaceinline(int ct)
728{
729    int i, sub;
730    struct region_highlight *rhp;
731
732    if (zlemetaline) {
733	sizeline(ct + zlemetall);
734	for (i = zlemetall; --i >= zlemetacs;)
735	    zlemetaline[i + ct] = zlemetaline[i];
736	zlemetall += ct;
737	zlemetaline[zlemetall] = '\0';
738
739	if (mark > zlemetacs)
740	    mark += ct;
741
742	if (region_highlights) {
743	    for (rhp = region_highlights + N_SPECIAL_HIGHLIGHTS;
744		 rhp < region_highlights + n_region_highlights;
745		 rhp++) {
746		if (rhp->flags & ZRH_PREDISPLAY)
747		    sub = predisplaylen;
748		else
749		    sub = 0;
750		if (rhp->start_meta - sub >= zlemetacs) {
751		    rhp->start_meta += ct;
752		}
753		if (rhp->end_meta - sub >= zlemetacs) {
754		    rhp->end_meta += ct;
755		}
756	    }
757	}
758    } else {
759	sizeline(ct + zlell);
760	for (i = zlell; --i >= zlecs;)
761	    zleline[i + ct] = zleline[i];
762	zlell += ct;
763	zleline[zlell] = ZWC('\0');
764
765	if (mark > zlecs)
766	    mark += ct;
767
768	if (region_highlights) {
769	    for (rhp = region_highlights + N_SPECIAL_HIGHLIGHTS;
770		 rhp < region_highlights + n_region_highlights;
771		 rhp++) {
772		if (rhp->flags & ZRH_PREDISPLAY)
773		    sub = predisplaylen;
774		else
775		    sub = 0;
776		if (rhp->start - sub >= zlecs) {
777		    rhp->start += ct;
778		}
779		if (rhp->end - sub >= zlecs) {
780		    rhp->end += ct;
781		}
782	    }
783	}
784    }
785    region_active = 0;
786}
787
788/*
789 * Within the ZLE line, cut the "cnt" characters from position "to".
790 */
791
792/**/
793void
794shiftchars(int to, int cnt)
795{
796    struct region_highlight *rhp;
797    int sub;
798
799    if (mark >= to + cnt)
800	mark -= cnt;
801    else if (mark > to)
802	mark = to;
803
804    if (zlemetaline) {
805	/* before to is updated... */
806	if (region_highlights) {
807	    for (rhp = region_highlights + N_SPECIAL_HIGHLIGHTS;
808		 rhp < region_highlights + n_region_highlights;
809		 rhp++) {
810		if (rhp->flags & ZRH_PREDISPLAY)
811		    sub = predisplaylen;
812		else
813		    sub = 0;
814		if (rhp->start_meta - sub > to) {
815		    if (rhp->start_meta - sub > to + cnt)
816			rhp->start_meta -= cnt;
817		    else
818			rhp->start_meta = to;
819		}
820		if (rhp->end_meta - sub > to) {
821		    if (rhp->end_meta - sub > to + cnt)
822			rhp->end_meta -= cnt;
823		    else
824			rhp->end_meta = to;
825		}
826	    }
827	}
828
829	while (to + cnt < zlemetall) {
830	    zlemetaline[to] = zlemetaline[to + cnt];
831	    to++;
832	}
833	zlemetaline[zlemetall = to] = '\0';
834    } else {
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 - sub > to) {
845		    if (rhp->start - sub > to + cnt)
846			rhp->start -= cnt;
847		    else
848			rhp->start = to;
849		}
850		if (rhp->end - sub > to) {
851		    if (rhp->end - sub > to + cnt)
852			rhp->end -= cnt;
853		    else
854			rhp->end = to;
855		}
856	    }
857	}
858
859	while (to + cnt < zlell) {
860	    zleline[to] = zleline[to + cnt];
861	    to++;
862	}
863	zleline[zlell = to] = ZWC('\0');
864    }
865    region_active = 0;
866}
867
868/*
869 * Put the ct characters starting at zleline + i into the
870 * cutbuffer, circling the kill ring if necessary (it's
871 * not if we're dealing with vi buffers, which is detected
872 * internally).  The text is not removed from zleline.
873 *
874 * dir indicates how the text is to be added to the cutbuffer,
875 * if the cutbuffer wasn't zeroed (this depends on the last
876 * command being a kill).  If dir is 1, the new text goes
877 * to the front of the cut buffer.  If dir is -1, the cutbuffer
878 * is completely overwritten.
879 */
880
881/**/
882void
883cut(int i, int ct, int flags)
884{
885  cuttext(zleline + i, ct, flags);
886}
887
888/*
889 * As cut, but explicitly supply the text together with its length.
890 */
891
892/**/
893void
894cuttext(ZLE_STRING_T line, int ct, int flags)
895{
896    if (!ct)
897	return;
898
899    UNMETACHECK();
900    if (zmod.flags & MOD_VIBUF) {
901	struct cutbuffer *b = &vibuf[zmod.vibuf];
902
903	if (!(zmod.flags & MOD_VIAPP) || !b->buf) {
904	    free(b->buf);
905	    b->buf = (ZLE_STRING_T)zalloc(ct * ZLE_CHAR_SIZE);
906	    ZS_memcpy(b->buf, line, ct);
907	    b->len = ct;
908	    b->flags = vilinerange ? CUTBUFFER_LINE : 0;
909	} else {
910	    int len = b->len;
911
912	    if(vilinerange)
913		b->flags |= CUTBUFFER_LINE;
914	    b->buf = (ZLE_STRING_T)
915		realloc((char *)b->buf,
916			(ct + len + !!(b->flags & CUTBUFFER_LINE))
917			* ZLE_CHAR_SIZE);
918	    if (b->flags & CUTBUFFER_LINE)
919		b->buf[len++] = ZWC('\n');
920	    ZS_memcpy(b->buf + len, line, ct);
921	    b->len = len + ct;
922	}
923	return;
924    } else {
925	/* Save in "1, shifting "1-"8 along to "2-"9 */
926	int n;
927	free(vibuf[34].buf);
928	for(n=34; n>26; n--)
929	    vibuf[n] = vibuf[n-1];
930	vibuf[26].buf = (ZLE_STRING_T)zalloc(ct * ZLE_CHAR_SIZE);
931	ZS_memcpy(vibuf[26].buf, line, ct);
932	vibuf[26].len = ct;
933	vibuf[26].flags = vilinerange ? CUTBUFFER_LINE : 0;
934    }
935    if (!cutbuf.buf) {
936	cutbuf.buf = (ZLE_STRING_T)zalloc(ZLE_CHAR_SIZE);
937	cutbuf.buf[0] = ZWC('\0');
938	cutbuf.len = cutbuf.flags = 0;
939    } else if (!(lastcmd & ZLE_KILL) || (flags & CUT_REPLACE)) {
940	Cutbuffer kptr;
941	if (!kring) {
942	    kringsize = KRINGCTDEF;
943	    kring = (Cutbuffer)zshcalloc(kringsize * sizeof(struct cutbuffer));
944	} else
945	    kringnum = (kringnum + 1) % kringsize;
946	kptr = kring + kringnum;
947	if (kptr->buf)
948	    free(kptr->buf);
949	*kptr = cutbuf;
950	cutbuf.buf = (ZLE_STRING_T)zalloc(ZLE_CHAR_SIZE);
951	cutbuf.buf[0] = ZWC('\0');
952	cutbuf.len = cutbuf.flags = 0;
953    }
954    if (flags & (CUT_FRONT|CUT_REPLACE)) {
955	ZLE_STRING_T s = (ZLE_STRING_T)zalloc((cutbuf.len + ct)*ZLE_CHAR_SIZE);
956
957	ZS_memcpy(s, line, ct);
958	ZS_memcpy(s + ct, cutbuf.buf, cutbuf.len);
959	free(cutbuf.buf);
960	cutbuf.buf = s;
961	cutbuf.len += ct;
962    } else {
963	cutbuf.buf = realloc((char *)cutbuf.buf,
964			     (cutbuf.len + ct) * ZLE_CHAR_SIZE);
965	ZS_memcpy(cutbuf.buf + cutbuf.len, line, ct);
966	cutbuf.len += ct;
967    }
968    if(vilinerange)
969	cutbuf.flags |= CUTBUFFER_LINE;
970    else
971	cutbuf.flags &= ~CUTBUFFER_LINE;
972}
973
974/*
975 * Now we're back in the world of zlecs where we need to keep
976 * track of whether we're on a combining character.
977 */
978
979/**/
980mod_export void
981backkill(int ct, int flags)
982{
983    UNMETACHECK();
984    if (flags & CUT_RAW) {
985	zlecs -= ct;
986    } else {
987	int origcs = zlecs;
988	while (ct--)
989	    DECCS();
990	ct = origcs - zlecs;
991    }
992
993    cut(zlecs, ct, flags);
994    shiftchars(zlecs, ct);
995    CCRIGHT();
996}
997
998/**/
999mod_export void
1000forekill(int ct, int flags)
1001{
1002    int i = zlecs;
1003
1004    UNMETACHECK();
1005    if (!(flags & CUT_RAW)) {
1006	int n = ct;
1007	while (n--)
1008	    INCCS();
1009	ct = zlecs - i;
1010	zlecs = i;
1011    }
1012
1013    cut(i, ct, flags);
1014    shiftchars(i, ct);
1015    CCRIGHT();
1016}
1017
1018/**/
1019mod_export void
1020backdel(int ct, int flags)
1021{
1022    if (flags & CUT_RAW) {
1023	if (zlemetaline != NULL) {
1024	    shiftchars(zlemetacs -= ct, ct);
1025	} else {
1026	    shiftchars(zlecs -= ct, ct);
1027	    CCRIGHT();
1028	}
1029    } else {
1030	int n = ct, origcs = zlecs;
1031	DPUTS(zlemetaline != NULL, "backdel needs CUT_RAW when metafied");
1032	while (n--)
1033	    DECCS();
1034	shiftchars(zlecs, origcs - zlecs);
1035	CCRIGHT();
1036    }
1037}
1038
1039/**/
1040mod_export void
1041foredel(int ct, int flags)
1042{
1043    if (flags & CUT_RAW) {
1044	if (zlemetaline != NULL) {
1045	    shiftchars(zlemetacs, ct);
1046	} else if (flags & CUT_RAW) {
1047	    shiftchars(zlecs, ct);
1048	    CCRIGHT();
1049	}
1050    } else {
1051	int origcs = zlecs;
1052	int n = ct;
1053	DPUTS(zlemetaline != NULL, "foredel needs CUT_RAW when metafied");
1054	while (n--)
1055	    INCCS();
1056	ct = zlecs - origcs;
1057	zlecs = origcs;
1058	shiftchars(zlecs, ct);
1059	CCRIGHT();
1060    }
1061}
1062
1063/**/
1064void
1065setline(char *s, int flags)
1066{
1067    char *scp;
1068
1069    UNMETACHECK();
1070    if (flags & ZSL_COPY)
1071	scp = ztrdup(s);
1072    else
1073	scp = s;
1074    /*
1075     * TBD: we could make this more efficient by passing the existing
1076     * allocated line to stringaszleline.
1077     */
1078    free(zleline);
1079
1080    zleline = stringaszleline(scp, 0, &zlell, &linesz, NULL);
1081
1082    if ((flags & ZSL_TOEND) && (zlecs = zlell) && invicmdmode())
1083	DECCS();
1084    else if (zlecs > zlell)
1085	zlecs = zlell;
1086    CCRIGHT();
1087    if (flags & ZSL_COPY)
1088	free(scp);
1089}
1090
1091/**/
1092int
1093findbol(void)
1094{
1095    int x = zlecs;
1096
1097    while (x > 0 && zleline[x - 1] != ZWC('\n'))
1098	x--;
1099    return x;
1100}
1101
1102/**/
1103int
1104findeol(void)
1105{
1106    int x = zlecs;
1107
1108    while (x != zlell && zleline[x] != ZWC('\n'))
1109	x++;
1110    return x;
1111}
1112
1113/**/
1114void
1115findline(int *a, int *b)
1116{
1117    *a = findbol();
1118    *b = findeol();
1119}
1120
1121/*
1122 * Query the user, and return 1 for yes, 0 for no.  The question is assumed to
1123 * have been printed already, and the cursor is left immediately after the
1124 * response echoed.  (Might cause a problem if this takes it onto the next
1125 * line.)  <Tab> is interpreted as 'y'; any other control character is
1126 * interpreted as 'n'.  If there are any characters in the buffer, this is
1127 * taken as a negative response, and no characters are read.  Case is folded.
1128 */
1129
1130/**/
1131mod_export int
1132getzlequery(void)
1133{
1134    ZLE_INT_T c;
1135#ifdef FIONREAD
1136    int val;
1137
1138    /* check for typeahead, which is treated as a negative response */
1139    ioctl(SHTTY, FIONREAD, (char *)&val);
1140    if (val) {
1141	putc('n', shout);
1142	return 0;
1143    }
1144#endif
1145
1146    /* get a character from the tty and interpret it */
1147    c = getfullchar(0);
1148    if (c == ZWC('\t'))
1149	c = ZWC('y');
1150    else if (ZC_icntrl(c) || c == ZLEEOF)
1151	c = ZWC('n');
1152    else
1153	c = ZC_tolower(c);
1154    /* echo response and return */
1155    if (c != ZWC('\n')) {
1156	REFRESH_ELEMENT re;
1157	re.chr = c;
1158	re.atr = 0;
1159	zwcputc(&re, NULL);
1160    }
1161    return c == ZWC('y');
1162}
1163
1164/* Format a string, keybinding style. */
1165
1166/**/
1167char *
1168bindztrdup(char *str)
1169{
1170    int c, len = 1;
1171    char *buf, *ptr, *ret;
1172
1173    for(ptr = str; *ptr; ptr++) {
1174	c = *ptr == Meta ? STOUC(*++ptr) ^ 32 : STOUC(*ptr);
1175	if(c & 0x80) {
1176	    len += 3;
1177	    c &= 0x7f;
1178	}
1179	if(c < 32 || c == 0x7f) {
1180	    len++;
1181	    c ^= 64;
1182	}
1183	len += c == '\\' || c == '^';
1184	len++;
1185    }
1186    ptr = buf = zalloc(len);
1187    for(; *str; str++) {
1188	c = *str == Meta ? STOUC(*++str) ^ 32 : STOUC(*str);
1189	if(c & 0x80) {
1190	    *ptr++ = '\\';
1191	    *ptr++ = 'M';
1192	    *ptr++ = '-';
1193	    c &= 0x7f;
1194	}
1195	if(c < 32 || c == 0x7f) {
1196	    *ptr++ = '^';
1197	    c ^= 64;
1198	}
1199	if(c == '\\' || c == '^')
1200	    *ptr++ = '\\';
1201	*ptr++ = c;
1202    }
1203    *ptr = 0;
1204    ret = dquotedztrdup(buf);
1205    zsfree(buf);
1206    return ret;
1207}
1208
1209/* Display a metafied string, keybinding-style. */
1210
1211/**/
1212int
1213printbind(char *str, FILE *stream)
1214{
1215    char *b = bindztrdup(str);
1216    int ret = zputs(b, stream);
1217
1218    zsfree(b);
1219    return ret;
1220}
1221
1222/*
1223 * Display a message where the completion list normally goes.
1224 * The message must be metafied.
1225 *
1226 * TODO: there's some advantage in using a ZLE_STRING_T array here,
1227 * together with improvements in other places, but messages don't
1228 * need to be particularly efficient.
1229 */
1230
1231/**/
1232mod_export void
1233showmsg(char const *msg)
1234{
1235    char const *p;
1236    int up = 0, cc = 0;
1237    ZLE_CHAR_T c;
1238#ifdef MULTIBYTE_SUPPORT
1239    char *umsg;
1240    int ulen, eol = 0;
1241    size_t width;
1242    mbstate_t mbs;
1243#endif
1244
1245    trashzle();
1246    clearflag = isset(USEZLE) && !termflags && isset(ALWAYSLASTPROMPT);
1247
1248#ifdef MULTIBYTE_SUPPORT
1249    umsg = ztrdup(msg);
1250    p = unmetafy(umsg, &ulen);
1251    memset(&mbs, 0, sizeof mbs);
1252
1253    mb_metacharinit();
1254    while (ulen > 0) {
1255	char const *n;
1256	if (*p == '\n') {
1257	    ulen--;
1258	    p++;
1259
1260	    putc('\n', shout);
1261	    up += 1 + cc / zterm_columns;
1262	    cc = 0;
1263	} else {
1264	    /*
1265	     * Extract the next wide character from the multibyte string.
1266	     */
1267	    size_t cnt = eol ? MB_INVALID : mbrtowc(&c, p, ulen, &mbs);
1268
1269	    switch (cnt) {
1270	    case MB_INCOMPLETE:
1271		eol = 1;
1272		/* FALL THROUGH */
1273	    case MB_INVALID:
1274		/*
1275		 * This really shouldn't be happening here, but...
1276		 * Treat it as a single byte character; it may get
1277		 * prettified.
1278		 */
1279		memset(&mbs, 0, sizeof mbs);
1280		n = nicechar(*p);
1281		cnt = 1;
1282		width = strlen(n);
1283		break;
1284	    case 0:
1285		cnt = 1;
1286		/* FALL THROUGH */
1287	    default:
1288		/*
1289		 * Paranoia: only needed if we start in the middle
1290		 * of a multibyte string and only in some implementations.
1291		 */
1292		if (cnt > (size_t)ulen)
1293		    cnt = ulen;
1294		n = wcs_nicechar(c, &width, NULL);
1295		break;
1296	    }
1297	    ulen -= cnt;
1298	    p += cnt;
1299
1300	    zputs(n, shout);
1301	    cc += width;
1302	}
1303    }
1304
1305    free(umsg);
1306#else
1307    for(p = msg; (c = *p); p++) {
1308	if(c == Meta)
1309	    c = *++p ^ 32;
1310	if(c == '\n') {
1311	    putc('\n', shout);
1312	    up += 1 + cc / zterm_columns;
1313	    cc = 0;
1314	} else {
1315	    char const *n = nicechar(c);
1316	    zputs(n, shout);
1317	    cc += strlen(n);
1318	}
1319    }
1320#endif
1321    up += cc / zterm_columns;
1322
1323    if (clearflag) {
1324	putc('\r', shout);
1325	tcmultout(TCUP, TCMULTUP, up + nlnct);
1326    } else
1327	putc('\n', shout);
1328    showinglist = 0;
1329}
1330
1331/* handle the error flag */
1332
1333/**/
1334int
1335handlefeep(UNUSED(char **args))
1336{
1337    zbeep();
1338    region_active = 0;
1339    return 0;
1340}
1341
1342/* user control of auto-suffixes -- see iwidgets.list */
1343
1344/**/
1345int
1346handlesuffix(UNUSED(char **args))
1347{
1348  return 0;
1349}
1350
1351/***************/
1352/* undo system */
1353/***************/
1354
1355/* head of the undo list, and the current position */
1356
1357static struct change *changes, *curchange;
1358
1359/* list of pending changes, not yet in the undo system */
1360
1361static struct change *nextchanges, *endnextchanges;
1362
1363/* incremented to provide a unique change number */
1364
1365static zlong undo_changeno;
1366
1367/* If non-zero, the last increment to undo_changeno was for the variable */
1368
1369static int undo_set_by_variable;
1370
1371/**/
1372void
1373initundo(void)
1374{
1375    nextchanges = NULL;
1376    changes = curchange = zalloc(sizeof(*curchange));
1377    curchange->prev = curchange->next = NULL;
1378    curchange->del = curchange->ins = NULL;
1379    curchange->dell = curchange->insl = 0;
1380    curchange->changeno = undo_changeno = 0;
1381    undo_set_by_variable = 0;
1382    lastline = zalloc((lastlinesz = linesz) * ZLE_CHAR_SIZE);
1383    ZS_memcpy(lastline, zleline, (lastll = zlell));
1384    lastcs = zlecs;
1385}
1386
1387/**/
1388void
1389freeundo(void)
1390{
1391    freechanges(changes);
1392    freechanges(nextchanges);
1393    zfree(lastline, lastlinesz);
1394}
1395
1396/**/
1397static void
1398freechanges(struct change *p)
1399{
1400    struct change *n;
1401
1402    for(; p; p = n) {
1403	n = p->next;
1404	free(p->del);
1405	free(p->ins);
1406	zfree(p, sizeof(*p));
1407    }
1408}
1409
1410/* register pending changes in the undo system */
1411
1412/**/
1413mod_export void
1414handleundo(void)
1415{
1416    int remetafy;
1417
1418    /*
1419     * Yuk: we call this from within the completion system,
1420     * so we need to convert back to the form which can be
1421     * copied into undo entries.
1422     */
1423    if (zlemetaline != NULL) {
1424	unmetafy_line();
1425	remetafy = 1;
1426    } else
1427	remetafy = 0;
1428
1429    mkundoent();
1430    if(nextchanges) {
1431	setlastline();
1432	if(curchange->next) {
1433	    freechanges(curchange->next);
1434	    curchange->next = NULL;
1435	    free(curchange->del);
1436	    free(curchange->ins);
1437	    curchange->del = curchange->ins = NULL;
1438	    curchange->dell = curchange->insl = 0;
1439	}
1440	nextchanges->prev = curchange->prev;
1441	if(curchange->prev)
1442	    curchange->prev->next = nextchanges;
1443	else
1444	    changes = nextchanges;
1445	curchange->prev = endnextchanges;
1446	endnextchanges->next = curchange;
1447	nextchanges = endnextchanges = NULL;
1448    }
1449
1450    if (remetafy)
1451	metafy_line();
1452}
1453
1454/* add an entry to the undo system, if anything has changed */
1455
1456/**/
1457void
1458mkundoent(void)
1459{
1460    int pre, suf;
1461    int sh = zlell < lastll ? zlell : lastll;
1462    struct change *ch;
1463
1464    UNMETACHECK();
1465    if(lastll == zlell && !ZS_memcmp(lastline, zleline, zlell)) {
1466	lastcs = zlecs;
1467	return;
1468    }
1469    for(pre = 0; pre < sh && zleline[pre] == lastline[pre]; )
1470	pre++;
1471    for(suf = 0; suf < sh - pre &&
1472	zleline[zlell - 1 - suf] == lastline[lastll - 1 - suf]; )
1473	suf++;
1474    ch = zalloc(sizeof(*ch));
1475    ch->next = NULL;
1476    ch->hist = histline;
1477    ch->off = pre;
1478    ch->old_cs = lastcs;
1479    ch->new_cs = zlecs;
1480    if(suf + pre == lastll) {
1481	ch->del = NULL;
1482	ch->dell = 0;
1483    } else {
1484	ch->dell = lastll - pre - suf;
1485	ch->del = (ZLE_STRING_T)zalloc(ch->dell * ZLE_CHAR_SIZE);
1486	ZS_memcpy(ch->del, lastline + pre, ch->dell);
1487    }
1488    if(suf + pre == zlell) {
1489	ch->ins = NULL;
1490	ch->insl = 0;
1491    } else {
1492	ch->insl = zlell - pre - suf;
1493	ch->ins = (ZLE_STRING_T)zalloc(ch->insl * ZLE_CHAR_SIZE);
1494	ZS_memcpy(ch->ins, zleline + pre, ch->insl);
1495    }
1496    if(nextchanges) {
1497	ch->flags = CH_PREV;
1498	ch->prev = endnextchanges;
1499	endnextchanges->flags |= CH_NEXT;
1500	endnextchanges->next = ch;
1501    } else {
1502	nextchanges = ch;
1503	ch->flags = 0;
1504	ch->prev = NULL;
1505    }
1506    ch->changeno = ++undo_changeno;
1507    undo_set_by_variable = 0;
1508    endnextchanges = ch;
1509}
1510
1511/* set lastline to match line */
1512
1513/**/
1514void
1515setlastline(void)
1516{
1517    UNMETACHECK();
1518    if(lastlinesz != linesz)
1519	lastline = realloc(lastline, (lastlinesz = linesz) * ZLE_CHAR_SIZE);
1520    ZS_memcpy(lastline, zleline, (lastll = zlell));
1521    lastcs = zlecs;
1522}
1523
1524/* move backwards through the change list */
1525
1526/**/
1527int
1528undo(char **args)
1529{
1530    zlong last_change;
1531
1532    if (*args)
1533	last_change = zstrtol(*args, NULL, 0);
1534    else
1535	last_change = (zlong)-1;
1536
1537    handleundo();
1538    do {
1539	struct change *prev = curchange->prev;
1540	if(!prev)
1541	    return 1;
1542	if (prev->changeno < last_change)
1543	    break;
1544	if (unapplychange(prev))
1545	    curchange = prev;
1546	else
1547	    break;
1548    } while (last_change >= (zlong)0 || (curchange->flags & CH_PREV));
1549    setlastline();
1550    return 0;
1551}
1552
1553/**/
1554static int
1555unapplychange(struct change *ch)
1556{
1557    if(ch->hist != histline) {
1558	zle_setline(quietgethist(ch->hist));
1559	zlecs = ch->new_cs;
1560	return 0;
1561    }
1562    zlecs = ch->off;
1563    if(ch->ins)
1564	foredel(ch->insl, CUT_RAW);
1565    if(ch->del) {
1566	spaceinline(ch->dell);
1567	ZS_memcpy(zleline + zlecs, ch->del, ch->dell);
1568	zlecs += ch->dell;
1569    }
1570    zlecs = ch->old_cs;
1571    return 1;
1572}
1573
1574/* move forwards through the change list */
1575
1576/**/
1577int
1578redo(UNUSED(char **args))
1579{
1580    handleundo();
1581    do {
1582	if(!curchange->next)
1583	    return 1;
1584	if (applychange(curchange))
1585	    curchange = curchange->next;
1586	else
1587	    break;
1588    } while(curchange->prev->flags & CH_NEXT);
1589    setlastline();
1590    return 0;
1591}
1592
1593/**/
1594static int
1595applychange(struct change *ch)
1596{
1597    if(ch->hist != histline) {
1598	zle_setline(quietgethist(ch->hist));
1599	zlecs = ch->old_cs;
1600	return 0;
1601    }
1602    zlecs = ch->off;
1603    if(ch->del)
1604	foredel(ch->dell, CUT_RAW);
1605    if(ch->ins) {
1606	spaceinline(ch->insl);
1607	ZS_memcpy(zleline + zlecs, ch->ins, ch->insl);
1608	zlecs += ch->insl;
1609    }
1610    zlecs = ch->new_cs;
1611    return 1;
1612}
1613
1614/* vi undo: toggle between the end of the undo list and the preceding point */
1615
1616/**/
1617int
1618viundochange(char **args)
1619{
1620    handleundo();
1621    if(curchange->next) {
1622	do {
1623	    applychange(curchange);
1624	    curchange = curchange->next;
1625	} while(curchange->next);
1626	setlastline();
1627	return 0;
1628    } else
1629	return undo(args);
1630}
1631
1632/*
1633 * Call a ZLE hook: a user-defined widget called at a specific point
1634 * within the line editor.
1635 *
1636 * A single argument arg is passed to the function (in addition to the
1637 * function name).  It may be NULL.
1638 */
1639
1640/**/
1641void
1642zlecallhook(char *name, char *arg)
1643{
1644    Thingy thingy = rthingy_nocreate(name);
1645    int saverrflag, savretflag;
1646    char *args[2];
1647
1648    if (!thingy)
1649	return;
1650
1651    saverrflag = errflag;
1652    savretflag = retflag;
1653
1654    args[0] = arg;
1655    args[1] = 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