mirror of
				git://git.code.sf.net/p/zsh/code
				synced 2025-10-31 18:10:56 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			1787 lines
		
	
	
	
		
			38 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1787 lines
		
	
	
	
		
			38 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * zle_utils.c - miscellaneous line editor utilities
 | |
|  *
 | |
|  * This file is part of zsh, the Z shell.
 | |
|  *
 | |
|  * Copyright (c) 1992-1997 Paul Falstad
 | |
|  * All rights reserved.
 | |
|  *
 | |
|  * Permission is hereby granted, without written agreement and without
 | |
|  * license or royalty fees, to use, copy, modify, and distribute this
 | |
|  * software and to distribute modified versions of this software for any
 | |
|  * purpose, provided that the above copyright notice and the following
 | |
|  * two paragraphs appear in all copies of this software.
 | |
|  *
 | |
|  * In no event shall Paul Falstad or the Zsh Development Group be liable
 | |
|  * to any party for direct, indirect, special, incidental, or consequential
 | |
|  * damages arising out of the use of this software and its documentation,
 | |
|  * even if Paul Falstad and the Zsh Development Group have been advised of
 | |
|  * the possibility of such damage.
 | |
|  *
 | |
|  * Paul Falstad and the Zsh Development Group specifically disclaim any
 | |
|  * warranties, including, but not limited to, the implied warranties of
 | |
|  * merchantability and fitness for a particular purpose.  The software
 | |
|  * provided hereunder is on an "as is" basis, and Paul Falstad and the
 | |
|  * Zsh Development Group have no obligation to provide maintenance,
 | |
|  * support, updates, enhancements, or modifications.
 | |
|  *
 | |
|  */
 | |
| 
 | |
| #include "zle.mdh"
 | |
| #include "zle_utils.pro"
 | |
| 
 | |
| /* Primary cut buffer */
 | |
| 
 | |
| /**/
 | |
| struct cutbuffer cutbuf;
 | |
| 
 | |
| /* Emacs-style kill buffer ring */
 | |
| 
 | |
| /**/
 | |
| struct cutbuffer *kring;
 | |
| /**/
 | |
| int kringsize, kringnum;
 | |
| 
 | |
| /* Vi named cut buffers.  0-25 are the named buffers "a to "z, and *
 | |
|  * 26-35 are the numbered buffer stack "0 to "9.                   */
 | |
| 
 | |
| /**/
 | |
| struct cutbuffer vibuf[36];
 | |
| 
 | |
| /* the line before last mod (for undo purposes) */
 | |
| 
 | |
| /**/
 | |
| ZLE_STRING_T lastline;
 | |
| /**/
 | |
| int lastlinesz, lastll, lastcs;
 | |
| 
 | |
| /* size of line buffer */
 | |
| 
 | |
| /**/
 | |
| int linesz;
 | |
| 
 | |
| /* make sure that the line buffer has at least sz chars */
 | |
| 
 | |
| /**/
 | |
| void
 | |
| sizeline(int sz)
 | |
| {
 | |
|     int cursz = (zlemetaline != NULL) ? metalinesz : linesz;
 | |
| 
 | |
|     while (sz > cursz) {
 | |
| 	if (cursz < 256)
 | |
| 	    cursz = 256;
 | |
| 	else
 | |
| 	    cursz *= 4;
 | |
| 
 | |
| 	if (zlemetaline != NULL) {
 | |
| 	    /* One spare character for the NULL */
 | |
| 	    zlemetaline = realloc(zlemetaline, cursz + 1);
 | |
| 	} else {
 | |
| 	    /* One spare character for the NULL, one for the newline */
 | |
| 	    zleline =
 | |
| 		(ZLE_STRING_T)realloc(zleline,
 | |
| 				      (cursz + 2) * ZLE_CHAR_SIZE);
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|     if (zlemetaline != NULL)
 | |
| 	metalinesz = cursz;
 | |
|     else
 | |
| 	linesz = cursz;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Insert a character, called from main shell.
 | |
|  * Note this always operates on the metafied multibyte version of the
 | |
|  * line.
 | |
|  */
 | |
| 
 | |
| /**/
 | |
| mod_export void
 | |
| zleaddtoline(int chr)
 | |
| {
 | |
|     spaceinline(1);
 | |
|     zlemetaline[zlemetacs++] = chr;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Convert a line editor character to a possibly multibyte character
 | |
|  * in a metafied string.  To be safe buf should have space for at least
 | |
|  * 2 * MB_CUR_MAX chars for multibyte mode and 2 otherwise.  Returns the
 | |
|  * length of the string added.
 | |
|  */
 | |
| 
 | |
| /**/
 | |
| int
 | |
| zlecharasstring(ZLE_CHAR_T inchar, char *buf)
 | |
| {
 | |
| #ifdef MULTIBYTE_SUPPORT
 | |
|     int ret;
 | |
|     char *ptr;
 | |
| 
 | |
| #ifdef __STDC_ISO_10646__
 | |
|     if (ZSH_INVALID_WCHAR_TEST(inchar)) {
 | |
| 	buf[0] = ZSH_INVALID_WCHAR_TO_CHAR(inchar);
 | |
| 	ret = 1;
 | |
|     } else
 | |
| #endif
 | |
|     {
 | |
| 	ret = wctomb(buf, inchar);
 | |
| 	if (ret <= 0) {
 | |
| 	    /* Ick. */
 | |
| 	    buf[0] = '?';
 | |
| 	    return 1;
 | |
| 	}
 | |
|     }
 | |
|     ptr = buf + ret - 1;
 | |
|     for (;;) {
 | |
| 	if (imeta(*ptr)) {
 | |
| 	    char *ptr2 = buf + ret - 1;
 | |
| 	    for (;;) {
 | |
| 		ptr2[1] = ptr2[0];
 | |
| 		if (ptr2 == ptr)
 | |
| 		    break;
 | |
| 		ptr2--;
 | |
| 	    }
 | |
| 	    *ptr = Meta;
 | |
| 	    ptr[1] ^= 32;
 | |
| 	    ret++;
 | |
| 	}
 | |
| 
 | |
| 	if (ptr == buf)
 | |
| 	    return ret;
 | |
| 	ptr--;
 | |
|     }
 | |
| #else
 | |
|     if (imeta(inchar)) {
 | |
| 	buf[0] = Meta;
 | |
| 	buf[1] = inchar ^ 32;
 | |
| 	return 2;
 | |
|     } else {
 | |
| 	buf[0] = inchar;
 | |
| 	return 1;
 | |
|     }
 | |
| #endif
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Input: a line in internal zle format, possibly using wide characters,
 | |
|  * possibly not, together with its length and the cursor position.
 | |
|  * The length must be accurate and includes all characters (no NULL
 | |
|  * termination is expected).  The input cursor position is only
 | |
|  * significant if outcs is non-NULL.
 | |
|  *
 | |
|  * Output: an ordinary NULL-terminated string, using multibyte characters
 | |
|  * instead of wide characters where appropriate and with the contents
 | |
|  * metafied.
 | |
|  *
 | |
|  * If outllp is non-NULL, assign the new length.  This is the conventional
 | |
|  * string length, without the NULL byte.
 | |
|  *
 | |
|  * If outcsp is non-NULL, assign the new character position.
 | |
|  * If outcsp is &zlemetacs, update the positions in the region_highlight
 | |
|  * array, too.  This is a bit of a hack.
 | |
|  *
 | |
|  * If useheap is 1, memory is returned from the heap, else is allocated
 | |
|  * for later freeing.
 | |
|  */
 | |
| 
 | |
| /**/
 | |
| mod_export char *
 | |
| zlelineasstring(ZLE_STRING_T instr, int inll, int incs, int *outllp,
 | |
| 		int *outcsp, int useheap)
 | |
| {
 | |
|     int outcs, outll, sub;
 | |
|     struct region_highlight *rhp;
 | |
| 
 | |
| #ifdef MULTIBYTE_SUPPORT
 | |
|     char *s;
 | |
|     int i, j;
 | |
|     size_t mb_len = 0;
 | |
|     mbstate_t mbs;
 | |
| 
 | |
|     s = zalloc(inll * MB_CUR_MAX + 1);
 | |
| 
 | |
|     outcs = 0;
 | |
|     memset(&mbs, 0, sizeof(mbs));
 | |
|     for (i=0; i < inll; i++) {
 | |
| 	if (incs == 0)
 | |
| 	    outcs = mb_len;
 | |
| 	incs--;
 | |
| 	if (region_highlights && outcsp == &zlemetacs) {
 | |
| 	    for (rhp = region_highlights + N_SPECIAL_HIGHLIGHTS;
 | |
| 		 rhp < region_highlights + n_region_highlights;
 | |
| 		 rhp++) {
 | |
| 		if (rhp->flags & ZRH_PREDISPLAY)
 | |
| 		    sub = predisplaylen;
 | |
| 		else
 | |
| 		    sub = 0;
 | |
| 		if (rhp->start - sub == 0)
 | |
| 		    rhp->start_meta = sub + mb_len;
 | |
| 		rhp->start--;
 | |
| 		if (rhp->end - sub == 0)
 | |
| 		    rhp->end_meta = sub + mb_len;
 | |
| 		rhp->end--;
 | |
| 	    }
 | |
| 	}
 | |
| #ifdef __STDC_ISO_10646__
 | |
| 	if (ZSH_INVALID_WCHAR_TEST(instr[i])) {
 | |
| 	    s[mb_len++] = ZSH_INVALID_WCHAR_TO_CHAR(instr[i]);
 | |
| 	} else
 | |
| #endif
 | |
| 	{
 | |
| 	    j = wcrtomb(s + mb_len, instr[i], &mbs);
 | |
| 	    if (j == -1) {
 | |
| 		/* invalid char */
 | |
| 		s[mb_len++] = ZWC('?');
 | |
| 		memset(&mbs, 0, sizeof(mbs));
 | |
| 	    } else {
 | |
| 		mb_len += j;
 | |
| 	    }
 | |
| 	}
 | |
|     }
 | |
|     if (incs == 0)
 | |
| 	outcs = mb_len;
 | |
|     if (region_highlights && outcsp == &zlemetacs) {
 | |
| 	for (rhp = region_highlights + N_SPECIAL_HIGHLIGHTS;
 | |
| 	     rhp < region_highlights + n_region_highlights;
 | |
| 	     rhp++) {
 | |
| 	    if (rhp->flags & ZRH_PREDISPLAY)
 | |
| 		sub = predisplaylen;
 | |
| 	    else
 | |
| 		sub = 0;
 | |
| 	    if (rhp->start - sub == 0)
 | |
| 		rhp->start_meta = sub + mb_len;
 | |
| 	    if (rhp->end - sub == 0)
 | |
| 		rhp->end_meta = sub + mb_len;
 | |
| 	}
 | |
|     }
 | |
|     s[mb_len] = '\0';
 | |
| 
 | |
|     outll = mb_len;
 | |
| #else
 | |
|     outll = inll;
 | |
|     outcs = incs;
 | |
|     if (region_highlights && outcsp == &zlemetacs) {
 | |
| 	for (rhp = region_highlights + N_SPECIAL_HIGHLIGHTS;
 | |
| 	     rhp < region_highlights + n_region_highlights;
 | |
| 	     rhp++) {
 | |
| 	    rhp->start_meta = rhp->start;
 | |
| 	    rhp->end_meta = rhp->end;
 | |
| 	}
 | |
|     }
 | |
| #endif
 | |
| 
 | |
|     /*
 | |
|      * *outcsp and *outllp are to be indexes into the final string,
 | |
|      * not character offsets, so we need to take account of any
 | |
|      * metafiable characters.
 | |
|      */
 | |
|     if (outcsp != NULL || outllp != NULL) {
 | |
| #ifdef MULTIBYTE_SUPPORT
 | |
| 	char *strp = s;
 | |
| #else
 | |
| 	char *strp = instr;
 | |
| #endif
 | |
| 	char *stopcs = strp + outcs;
 | |
| 	char *stopll = strp + outll;
 | |
| 	char *startp = strp;
 | |
| 
 | |
| 	if (region_highlights && outcsp == &zlemetacs) {
 | |
| 	    for (rhp = region_highlights + N_SPECIAL_HIGHLIGHTS;
 | |
| 		 rhp < region_highlights + n_region_highlights;
 | |
| 		 rhp++) {
 | |
| 		/* Used as temporary storage */
 | |
| 		rhp->start = rhp->start_meta;
 | |
| 		rhp->end = rhp->end_meta;
 | |
| 	    }
 | |
| 	}
 | |
| 	while (strp < stopll) {
 | |
| 	    if (imeta(*strp)) {
 | |
| 		if (strp < stopcs)
 | |
| 		    outcs++;
 | |
| 		if (region_highlights && outcsp == &zlemetacs) {
 | |
| 		    for (rhp = region_highlights + N_SPECIAL_HIGHLIGHTS;
 | |
| 			 rhp < region_highlights + n_region_highlights;
 | |
| 			 rhp++) {
 | |
| 			if (rhp->flags & ZRH_PREDISPLAY)
 | |
| 			    sub = predisplaylen;
 | |
| 			else
 | |
| 			    sub = 0;
 | |
| 			if (strp < startp + rhp->start - sub) {
 | |
| 			    rhp->start_meta++;
 | |
| 			}
 | |
| 			if (strp < startp + rhp->end - sub) {
 | |
| 			    rhp->end_meta++;
 | |
| 			}
 | |
| 		    }
 | |
| 		}
 | |
| 		outll++;
 | |
| 	    }
 | |
| 	    strp++;
 | |
| 	}
 | |
| 	if (outcsp != NULL)
 | |
| 	    *outcsp = outcs;
 | |
| 	if (outllp != NULL)
 | |
| 	    *outllp = outll;
 | |
|     }
 | |
| 
 | |
| #ifdef MULTIBYTE_SUPPORT
 | |
|     if (useheap) {
 | |
| 	char *ret = metafy(s, mb_len, META_HEAPDUP);
 | |
| 
 | |
| 	zfree(s, inll * MB_CUR_MAX + 1);
 | |
| 
 | |
| 	return ret;
 | |
|     }
 | |
|     return metafy(s, mb_len, META_REALLOC);
 | |
| #else
 | |
|     return metafy(instr, inll, useheap ? META_HEAPDUP : META_DUP);
 | |
| #endif
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|  * Input a NULL-terminated metafied string instr.
 | |
|  * Output a line in internal zle format, together with its length
 | |
|  * in the appropriate character units.  Note that outll may not be NULL.
 | |
|  *
 | |
|  * If outsz is non-NULL, the number of allocated characters in the
 | |
|  * string is written there.  For compatibility with use of the linesz
 | |
|  * variable (allocate size of zleline), at least two characters are
 | |
|  * allocated more than needed for immediate use.  (The extra characters
 | |
|  * may take a newline and a null at a later stage.)  These are not
 | |
|  * included in *outsz.
 | |
|  *
 | |
|  * If outcs is non-NULL, the character position in the original
 | |
|  * string incs (a standard string offset, i.e. incremented 2 for
 | |
|  * each metafied character) is converted into the corresponding
 | |
|  * character position in *outcs.
 | |
|  *
 | |
|  * If, further, outcs is &zlecs, we update the positions in the
 | |
|  * region_highlight array, too.  (This is a bit of a hack.)
 | |
|  *
 | |
|  * Note that instr is modified in place, hence should be copied
 | |
|  * first if necessary;
 | |
|  *
 | |
|  * Memory for the returned string is permanently allocated.  *outsz may
 | |
|  * be longer than the *outll returned.  Hence it should be freed with
 | |
|  * zfree(outstr, *outsz) or free(outstr), not zfree(outstr, *outll).
 | |
|  */
 | |
| 
 | |
| /**/
 | |
| mod_export ZLE_STRING_T
 | |
| stringaszleline(char *instr, int incs, int *outll, int *outsz, int *outcs)
 | |
| {
 | |
|     ZLE_STRING_T outstr;
 | |
|     int ll, sz, sub;
 | |
|     struct region_highlight *rhp;
 | |
| #ifdef MULTIBYTE_SUPPORT
 | |
|     mbstate_t mbs;
 | |
| #endif
 | |
| 
 | |
|     if (outcs) {
 | |
| 	/*
 | |
| 	 * Take account of Meta characters in the input string
 | |
| 	 * before we unmetafy it.  This does not yet take account
 | |
| 	 * of multibyte characters.  If there are none, this
 | |
| 	 * is all the processing required to calculate outcs.
 | |
| 	 */
 | |
| 	char *inptr = instr, *cspos = instr + incs;
 | |
| 	if (region_highlights && outcs == &zlecs) {
 | |
| 	    for (rhp = region_highlights + N_SPECIAL_HIGHLIGHTS;
 | |
| 		 rhp < region_highlights + n_region_highlights;
 | |
| 		 rhp++) {
 | |
| 		rhp->start = rhp->start_meta;
 | |
| 		rhp->end = rhp->end_meta;
 | |
| 	    }
 | |
| 	}
 | |
| 	while (*inptr) {
 | |
| 	    if (*inptr == Meta) {
 | |
| 		if (inptr < cspos) {
 | |
| 		    incs--;
 | |
| 		}
 | |
| 		if (region_highlights && outcs == &zlecs) {
 | |
| 		    for (rhp = region_highlights + N_SPECIAL_HIGHLIGHTS;
 | |
| 			 rhp < region_highlights + n_region_highlights;
 | |
| 			 rhp++) {
 | |
| 			if (rhp->flags & ZRH_PREDISPLAY)
 | |
| 			    sub = predisplaylen;
 | |
| 			else
 | |
| 			    sub = 0;
 | |
| 			if (inptr - instr < rhp->start - sub) {
 | |
| 			    rhp->start_meta--;
 | |
| 			}
 | |
| 			if (inptr - instr < rhp->end - sub) {
 | |
| 			    rhp->end_meta--;
 | |
| 			}
 | |
| 		    }
 | |
| 		}
 | |
| 		inptr++;
 | |
| 	    }
 | |
| 	    inptr++;
 | |
| 	}
 | |
|     }
 | |
|     unmetafy(instr, &ll);
 | |
| 
 | |
|     /*
 | |
|      * ll is the maximum number of characters there can be in
 | |
|      * the output string; the closer to ASCII the string, the
 | |
|      * better the guess.  For the 2 see above.
 | |
|      */
 | |
|     sz = (ll + 2) * ZLE_CHAR_SIZE;
 | |
|     if (outsz)
 | |
| 	*outsz = ll;
 | |
|     outstr = (ZLE_STRING_T)zalloc(sz);
 | |
| 
 | |
| #ifdef MULTIBYTE_SUPPORT
 | |
|     if (ll) {
 | |
| 	char *inptr = instr;
 | |
| 	wchar_t *outptr = outstr;
 | |
| 
 | |
| 	/* Reset shift state to input complete string */
 | |
| 	memset(&mbs, '\0', sizeof mbs);
 | |
| 
 | |
| 	while (ll > 0) {
 | |
| 	    size_t cnt = mbrtowc(outptr, inptr, ll, &mbs);
 | |
| 
 | |
| #ifdef __STDC_ISO_10646__
 | |
| 	    if (cnt == MB_INCOMPLETE || cnt == MB_INVALID) {
 | |
| 		/* Use private encoding for invalid single byte */
 | |
| 		*outptr = ZSH_CHAR_TO_INVALID_WCHAR(*inptr);
 | |
| 		cnt = 1;
 | |
| 	    }
 | |
| #else
 | |
| 	    /*
 | |
| 	     * At this point we don't handle either incomplete (-2) or
 | |
| 	     * invalid (-1) multibyte sequences.  Use the current length
 | |
| 	     * and return.
 | |
| 	     */
 | |
| 	    if (cnt == MB_INCOMPLETE || cnt == MB_INVALID)
 | |
| 		break;
 | |
| #endif
 | |
| 
 | |
| 	    if (cnt == 0) {
 | |
| 		/* Converting '\0' returns 0, but a '\0' is a real
 | |
| 		 * character for us, so we should consume 1 byte
 | |
| 		 * (certainly true for Unicode and unlikely to be false
 | |
| 		 * in any non-pathological multibyte representation). */
 | |
| 		cnt = 1;
 | |
| 	    } else if (cnt > (size_t)ll) {
 | |
| 		/*
 | |
| 		 * Some multibyte implementations return the
 | |
| 		 * full length of a previous incomplete character
 | |
| 		 * instead of the remaining length.
 | |
| 		 * This is paranoia: it only applies if we start
 | |
| 		 * midway through a multibyte character, which
 | |
| 		 * presumably can't happen.
 | |
| 		 */
 | |
| 		cnt = ll;
 | |
| 	    }
 | |
| 
 | |
| 	    if (outcs) {
 | |
| 		int offs = inptr - instr;
 | |
| 		if (offs <= incs && incs < offs + (int)cnt)
 | |
| 		    *outcs = outptr - outstr;
 | |
| 		if (region_highlights && outcs == &zlecs) {
 | |
| 		    for (rhp = region_highlights + N_SPECIAL_HIGHLIGHTS;
 | |
| 			 rhp < region_highlights + n_region_highlights;
 | |
| 			 rhp++) {
 | |
| 			if (rhp->flags & ZRH_PREDISPLAY)
 | |
| 			    sub = predisplaylen;
 | |
| 			else
 | |
| 			    sub = 0;
 | |
| 			if (offs <= rhp->start_meta - sub &&
 | |
| 			    rhp->start_meta - sub < offs + (int)cnt) {
 | |
| 			    rhp->start = outptr - outstr + sub;
 | |
| 			}
 | |
| 			if (offs <= rhp->end_meta - sub &&
 | |
| 			    rhp->end_meta - sub < offs + (int)cnt) {
 | |
| 			    rhp->end = outptr - outstr + sub;
 | |
| 			}
 | |
| 		    }
 | |
| 		}
 | |
| 	    }
 | |
| 
 | |
| 	    inptr += cnt;
 | |
| 	    outptr++;
 | |
| 	    ll -= cnt;
 | |
| 	}
 | |
| 	if (outcs && inptr <= instr + incs)
 | |
| 	    *outcs = outptr - outstr;
 | |
| 	*outll = outptr - outstr;
 | |
|     } else {
 | |
| 	*outll = 0;
 | |
| 	if (outcs)
 | |
| 	    *outcs = 0;
 | |
|     }
 | |
| #else
 | |
|     memcpy(outstr, instr, ll);
 | |
|     *outll = ll;
 | |
|     if (outcs)
 | |
| 	*outcs = incs;
 | |
|     if (region_highlights && outcs == &zlecs) {
 | |
| 	for (rhp = region_highlights + N_SPECIAL_HIGHLIGHTS;
 | |
| 	     rhp < region_highlights + n_region_highlights;
 | |
| 	     rhp++) {
 | |
| 	    rhp->start = rhp->start_meta;
 | |
| 	    rhp->end = rhp->end_meta;
 | |
| 	}
 | |
|     }
 | |
| #endif
 | |
| 
 | |
|     return outstr;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * This function is called when we are playing very nasty tricks
 | |
|  * indeed: see bufferwords in hist.c.  Consequently we can make
 | |
|  * absolutely no assumption about the state whatsoever, except
 | |
|  * that it has one.
 | |
|  */
 | |
| 
 | |
| /**/
 | |
| mod_export char *
 | |
| zlegetline(int *ll, int *cs)
 | |
| {
 | |
|     if (zlemetaline != NULL) {
 | |
| 	*ll = zlemetall;
 | |
| 	*cs = zlemetacs;
 | |
| 	return ztrdup(zlemetaline);
 | |
|     }
 | |
|     if (zleline)
 | |
| 	return zlelineasstring(zleline, zlell, zlecs, ll, cs, 0);
 | |
|     *ll = *cs = 0;
 | |
|     return ztrdup("");
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Forward reference */
 | |
| struct zle_region;
 | |
| 
 | |
| /* A non-special entry in region_highlight */
 | |
| struct zle_region  {
 | |
|     struct zle_region *next;
 | |
|     /* Entries of region_highlight, as needed */
 | |
|     int atr;
 | |
|     int start;
 | |
|     int end;
 | |
|     int flags;
 | |
| };
 | |
| 
 | |
| /* Forward reference */
 | |
| struct zle_position;
 | |
| 
 | |
| /* A saved set of position information */
 | |
| struct zle_position {
 | |
|     /* Link pointer */
 | |
|     struct zle_position *next;
 | |
|     /* Cursor position */
 | |
|     int cs;
 | |
|     /* Mark */
 | |
|     int mk;
 | |
|     /* Line length */
 | |
|     int ll;
 | |
|     struct zle_region *regions;
 | |
| };
 | |
| 
 | |
| /* LIFO stack of positions */
 | |
| static struct zle_position *zle_positions;
 | |
| 
 | |
| /*
 | |
|  * Save positions including cursor, end-of-line and
 | |
|  * (non-special) region highlighting.
 | |
|  *
 | |
|  * Must be matched by a subsequent zle_restore_positions().
 | |
|  */
 | |
| 
 | |
| /**/
 | |
| mod_export void
 | |
| zle_save_positions(void)
 | |
| {
 | |
|     struct region_highlight *rhp;
 | |
|     struct zle_position *newpos;
 | |
|     struct zle_region **newrhpp, *newrhp;
 | |
| 
 | |
|     newpos = (struct zle_position *)zalloc(sizeof(*newpos));
 | |
| 
 | |
|     newpos->mk = mark;
 | |
|     if (zlemetaline) {
 | |
| 	/* Use metafied information */
 | |
| 	newpos->cs = zlemetacs;
 | |
| 	newpos->ll = zlemetall;
 | |
|     } else {
 | |
| 	/* Use unmetafied information */
 | |
| 	newpos->cs = zlecs;
 | |
| 	newpos->ll = zlell;
 | |
| 
 | |
|     }
 | |
| 
 | |
|     newrhpp = &newpos->regions;
 | |
|     *newrhpp = NULL;
 | |
|     if (region_highlights) {
 | |
| 	for (rhp = region_highlights + N_SPECIAL_HIGHLIGHTS;
 | |
| 	     rhp < region_highlights + n_region_highlights;
 | |
| 	     rhp++) {
 | |
| 	    /*
 | |
| 	     * This is a FIFO stack, so we preserve the order
 | |
| 	     * of entries when we restore region_highlights.
 | |
| 	     */
 | |
| 	    newrhp = *newrhpp = (struct zle_region *)zalloc(sizeof(**newrhpp));
 | |
| 	    newrhp->next = NULL;
 | |
| 	    newrhp->atr = rhp->atr;
 | |
| 	    newrhp->flags = rhp->flags;
 | |
| 	    if (zlemetaline) {
 | |
| 		newrhp->start = rhp->start_meta;
 | |
| 		newrhp->end = rhp->end_meta;
 | |
| 	    } else {
 | |
| 		newrhp->start = rhp->start;
 | |
| 		newrhp->end = rhp->end;
 | |
| 	    }
 | |
| 	    newrhpp = &newrhp->next;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|     newpos->next = zle_positions;
 | |
|     zle_positions = newpos;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Restore positions previously saved.
 | |
|  * Relies on zlemetaline being restored correctly beforehand,
 | |
|  * so that it can tell whether to use metafied positions or not.
 | |
|  */
 | |
| 
 | |
| /**/
 | |
| mod_export void
 | |
| zle_restore_positions(void)
 | |
| {
 | |
|     struct zle_position *oldpos = zle_positions;
 | |
|     struct zle_region *oldrhp;
 | |
|     struct region_highlight *rhp;
 | |
|     int nreg;
 | |
| 
 | |
|     zle_positions = oldpos->next;
 | |
| 
 | |
|     mark = oldpos->mk;
 | |
|     if (zlemetaline) {
 | |
| 	/* Use metafied information */
 | |
| 	zlemetacs = oldpos->cs;
 | |
| 	zlemetall = oldpos->ll;
 | |
|     } else {
 | |
| 	/* Use unmetafied information */
 | |
| 	zlecs = oldpos->cs;
 | |
| 	zlell = oldpos->ll;
 | |
|     }
 | |
| 
 | |
|     if (oldpos->regions) {
 | |
| 	/* Count number of regions and see if the array needs resizing */
 | |
| 	for (nreg = 0, oldrhp = oldpos->regions;
 | |
| 	     oldrhp;
 | |
| 	     nreg++, oldrhp = oldrhp->next)
 | |
| 	    ;
 | |
| 	if (nreg + N_SPECIAL_HIGHLIGHTS != n_region_highlights) {
 | |
| 	    n_region_highlights = nreg + N_SPECIAL_HIGHLIGHTS;
 | |
| 	    region_highlights = (struct region_highlight *)
 | |
| 		zrealloc(region_highlights,
 | |
| 			 sizeof(struct region_highlight) * n_region_highlights);
 | |
| 	}
 | |
| 	oldrhp = oldpos->regions;
 | |
| 	rhp = region_highlights + N_SPECIAL_HIGHLIGHTS;
 | |
| 	while (oldrhp) {
 | |
| 	    struct zle_region *nextrhp = oldrhp->next;
 | |
| 
 | |
| 	    rhp->atr = oldrhp->atr;
 | |
| 	    rhp->flags = oldrhp->flags;
 | |
| 	    if (zlemetaline) {
 | |
| 		rhp->start_meta = oldrhp->start;
 | |
| 		rhp->end_meta = oldrhp->end;
 | |
| 	    } else {
 | |
| 		rhp->start = oldrhp->start;
 | |
| 		rhp->end = oldrhp->end;
 | |
| 	    }
 | |
| 
 | |
| 	    zfree(oldrhp, sizeof(*oldrhp));
 | |
| 	    oldrhp = nextrhp;
 | |
| 	    rhp++;
 | |
| 	}
 | |
|     } else if (region_highlights) {
 | |
| 	zfree(region_highlights, sizeof(struct region_highlight) *
 | |
| 	      n_region_highlights);
 | |
| 	region_highlights  = NULL;
 | |
| 	n_region_highlights = 0;
 | |
|     }
 | |
| 
 | |
|     zfree(oldpos, sizeof(*oldpos));
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Discard positions previously saved, the line has been updated.
 | |
|  */
 | |
| 
 | |
| /**/
 | |
| mod_export void
 | |
| zle_free_positions(void)
 | |
| {
 | |
|     struct zle_position *oldpos = zle_positions;
 | |
|     struct zle_region *oldrhp;
 | |
| 
 | |
|     zle_positions = oldpos->next;
 | |
|     oldrhp = oldpos->regions;
 | |
|     while (oldrhp) {
 | |
| 	struct zle_region *nextrhp = oldrhp->next;
 | |
| 	zfree(oldrhp, sizeof(*oldrhp));
 | |
| 	oldrhp = nextrhp;
 | |
|     }
 | |
|     zfree(oldpos, sizeof(*oldpos));
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Basic utility functions for adding to line or removing from line.
 | |
|  * At this level the counts supplied are raw character counts, so
 | |
|  * the calling code must be aware of combining characters where
 | |
|  * necessary, e.g. if we want to delete a + combing grave forward
 | |
|  * from the cursor, then shiftchars() gets the count 2 (not 1).
 | |
|  *
 | |
|  * This is necessary because these utility functions don't know about
 | |
|  * zlecs, and we need to count combined characters from there.
 | |
|  */
 | |
| 
 | |
| /* insert space for ct chars at cursor position */
 | |
| 
 | |
| /**/
 | |
| mod_export void
 | |
| spaceinline(int ct)
 | |
| {
 | |
|     int i, sub;
 | |
|     struct region_highlight *rhp;
 | |
| 
 | |
|     if (zlemetaline) {
 | |
| 	sizeline(ct + zlemetall);
 | |
| 	for (i = zlemetall; --i >= zlemetacs;)
 | |
| 	    zlemetaline[i + ct] = zlemetaline[i];
 | |
| 	zlemetall += ct;
 | |
| 	zlemetaline[zlemetall] = '\0';
 | |
| 
 | |
| 	if (mark > zlemetacs)
 | |
| 	    mark += ct;
 | |
| 
 | |
| 	if (region_highlights) {
 | |
| 	    for (rhp = region_highlights + N_SPECIAL_HIGHLIGHTS;
 | |
| 		 rhp < region_highlights + n_region_highlights;
 | |
| 		 rhp++) {
 | |
| 		if (rhp->flags & ZRH_PREDISPLAY)
 | |
| 		    sub = predisplaylen;
 | |
| 		else
 | |
| 		    sub = 0;
 | |
| 		if (rhp->start_meta - sub >= zlemetacs) {
 | |
| 		    rhp->start_meta += ct;
 | |
| 		}
 | |
| 		if (rhp->end_meta - sub >= zlemetacs) {
 | |
| 		    rhp->end_meta += ct;
 | |
| 		}
 | |
| 	    }
 | |
| 	}
 | |
|     } else {
 | |
| 	sizeline(ct + zlell);
 | |
| 	for (i = zlell; --i >= zlecs;)
 | |
| 	    zleline[i + ct] = zleline[i];
 | |
| 	zlell += ct;
 | |
| 	zleline[zlell] = ZWC('\0');
 | |
| 
 | |
| 	if (mark > zlecs)
 | |
| 	    mark += ct;
 | |
| 	if (viinsbegin > zlecs)
 | |
| 	    viinsbegin = 0;
 | |
| 
 | |
| 	if (region_highlights) {
 | |
| 	    for (rhp = region_highlights + N_SPECIAL_HIGHLIGHTS;
 | |
| 		 rhp < region_highlights + n_region_highlights;
 | |
| 		 rhp++) {
 | |
| 		if (rhp->flags & ZRH_PREDISPLAY)
 | |
| 		    sub = predisplaylen;
 | |
| 		else
 | |
| 		    sub = 0;
 | |
| 		if (rhp->start - sub >= zlecs) {
 | |
| 		    rhp->start += ct;
 | |
| 		}
 | |
| 		if (rhp->end - sub >= zlecs) {
 | |
| 		    rhp->end += ct;
 | |
| 		}
 | |
| 	    }
 | |
| 	}
 | |
|     }
 | |
|     region_active = 0;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Within the ZLE line, cut the "cnt" characters from position "to".
 | |
|  */
 | |
| 
 | |
| /**/
 | |
| void
 | |
| shiftchars(int to, int cnt)
 | |
| {
 | |
|     struct region_highlight *rhp;
 | |
|     int sub;
 | |
| 
 | |
|     if (mark >= to + cnt)
 | |
| 	mark -= cnt;
 | |
|     else if (mark > to)
 | |
| 	mark = to;
 | |
| 
 | |
|     if (zlemetaline) {
 | |
| 	/* before to is updated... */
 | |
| 	if (region_highlights) {
 | |
| 	    for (rhp = region_highlights + N_SPECIAL_HIGHLIGHTS;
 | |
| 		 rhp < region_highlights + n_region_highlights;
 | |
| 		 rhp++) {
 | |
| 		if (rhp->flags & ZRH_PREDISPLAY)
 | |
| 		    sub = predisplaylen;
 | |
| 		else
 | |
| 		    sub = 0;
 | |
| 		if (rhp->start_meta - sub > to) {
 | |
| 		    if (rhp->start_meta - sub > to + cnt)
 | |
| 			rhp->start_meta -= cnt;
 | |
| 		    else
 | |
| 			rhp->start_meta = to;
 | |
| 		}
 | |
| 		if (rhp->end_meta - sub > to) {
 | |
| 		    if (rhp->end_meta - sub > to + cnt)
 | |
| 			rhp->end_meta -= cnt;
 | |
| 		    else
 | |
| 			rhp->end_meta = to;
 | |
| 		}
 | |
| 	    }
 | |
| 	}
 | |
| 
 | |
| 	while (to + cnt < zlemetall) {
 | |
| 	    zlemetaline[to] = zlemetaline[to + cnt];
 | |
| 	    to++;
 | |
| 	}
 | |
| 	zlemetaline[zlemetall = to] = '\0';
 | |
|     } else {
 | |
| 	/* before to is updated... */
 | |
| 	if (region_highlights) {
 | |
| 	    for (rhp = region_highlights + N_SPECIAL_HIGHLIGHTS;
 | |
| 		 rhp < region_highlights + n_region_highlights;
 | |
| 		 rhp++) {
 | |
| 		if (rhp->flags & ZRH_PREDISPLAY)
 | |
| 		    sub = predisplaylen;
 | |
| 		else
 | |
| 		    sub = 0;
 | |
| 		if (rhp->start - sub > to) {
 | |
| 		    if (rhp->start - sub > to + cnt)
 | |
| 			rhp->start -= cnt;
 | |
| 		    else
 | |
| 			rhp->start = to;
 | |
| 		}
 | |
| 		if (rhp->end - sub > to) {
 | |
| 		    if (rhp->end - sub > to + cnt)
 | |
| 			rhp->end -= cnt;
 | |
| 		    else
 | |
| 			rhp->end = to;
 | |
| 		}
 | |
| 	    }
 | |
| 	}
 | |
| 
 | |
| 	while (to + cnt < zlell) {
 | |
| 	    zleline[to] = zleline[to + cnt];
 | |
| 	    to++;
 | |
| 	}
 | |
| 	zleline[zlell = to] = ZWC('\0');
 | |
|     }
 | |
|     region_active = 0;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Put the ct characters starting at zleline + i into the
 | |
|  * cutbuffer, circling the kill ring if necessary (it's
 | |
|  * not if we're dealing with vi buffers, which is detected
 | |
|  * internally).  The text is not removed from zleline.
 | |
|  *
 | |
|  * dir indicates how the text is to be added to the cutbuffer,
 | |
|  * if the cutbuffer wasn't zeroed (this depends on the last
 | |
|  * command being a kill).  If dir is 1, the new text goes
 | |
|  * to the front of the cut buffer.  If dir is -1, the cutbuffer
 | |
|  * is completely overwritten.
 | |
|  */
 | |
| 
 | |
| /**/
 | |
| void
 | |
| cut(int i, int ct, int flags)
 | |
| {
 | |
|   cuttext(zleline + i, ct, flags);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * As cut, but explicitly supply the text together with its length.
 | |
|  */
 | |
| 
 | |
| /**/
 | |
| void
 | |
| cuttext(ZLE_STRING_T line, int ct, int flags)
 | |
| {
 | |
|     if (!(ct || vilinerange) ||  zmod.flags & MOD_NULL)
 | |
| 	return;
 | |
| 
 | |
|     UNMETACHECK();
 | |
|     if (zmod.flags & MOD_VIBUF) {
 | |
| 	struct cutbuffer *b = &vibuf[zmod.vibuf];
 | |
| 
 | |
| 	if (!(zmod.flags & MOD_VIAPP) || !b->buf) {
 | |
| 	    free(b->buf);
 | |
| 	    b->buf = (ZLE_STRING_T)zalloc(ct * ZLE_CHAR_SIZE);
 | |
| 	    ZS_memcpy(b->buf, line, ct);
 | |
| 	    b->len = ct;
 | |
| 	    b->flags = vilinerange ? CUTBUFFER_LINE : 0;
 | |
| 	} else {
 | |
| 	    int len = b->len;
 | |
| 
 | |
| 	    if(vilinerange)
 | |
| 		b->flags |= CUTBUFFER_LINE;
 | |
| 	    b->buf = (ZLE_STRING_T)
 | |
| 		realloc((char *)b->buf,
 | |
| 			(ct + len + !!(b->flags & CUTBUFFER_LINE))
 | |
| 			* ZLE_CHAR_SIZE);
 | |
| 	    if (b->flags & CUTBUFFER_LINE)
 | |
| 		b->buf[len++] = ZWC('\n');
 | |
| 	    ZS_memcpy(b->buf + len, line, ct);
 | |
| 	    b->len = len + ct;
 | |
| 	}
 | |
|     } else if (flags & CUT_YANK) {
 | |
| 	/* Save in "0 */
 | |
| 	free(vibuf[26].buf);
 | |
| 	vibuf[26].buf = (ZLE_STRING_T)zalloc(ct * ZLE_CHAR_SIZE);
 | |
| 	ZS_memcpy(vibuf[26].buf, line, ct);
 | |
| 	vibuf[26].len = ct;
 | |
| 	vibuf[26].flags = vilinerange ? CUTBUFFER_LINE : 0;
 | |
|     } else {
 | |
| 	/* Save in "1, shifting "1-"8 along to "2-"9 */
 | |
| 	int n;
 | |
| 	free(vibuf[35].buf);
 | |
| 	for(n=35; n>27; n--)
 | |
| 	    vibuf[n] = vibuf[n-1];
 | |
| 	vibuf[27].buf = (ZLE_STRING_T)zalloc(ct * ZLE_CHAR_SIZE);
 | |
| 	ZS_memcpy(vibuf[27].buf, line, ct);
 | |
| 	vibuf[27].len = ct;
 | |
| 	vibuf[27].flags = vilinerange ? CUTBUFFER_LINE : 0;
 | |
|     }
 | |
|     if (!cutbuf.buf) {
 | |
| 	cutbuf.buf = (ZLE_STRING_T)zalloc(ZLE_CHAR_SIZE);
 | |
| 	cutbuf.buf[0] = ZWC('\0');
 | |
| 	cutbuf.len = cutbuf.flags = 0;
 | |
|     } else if (!(lastcmd & ZLE_KILL) || (flags & CUT_REPLACE)) {
 | |
| 	Cutbuffer kptr;
 | |
| 	if (!kring) {
 | |
| 	    kringsize = KRINGCTDEF;
 | |
| 	    kring = (Cutbuffer)zshcalloc(kringsize * sizeof(struct cutbuffer));
 | |
| 	} else
 | |
| 	    kringnum = (kringnum + 1) % kringsize;
 | |
| 	kptr = kring + kringnum;
 | |
| 	if (kptr->buf)
 | |
| 	    free(kptr->buf);
 | |
| 	*kptr = cutbuf;
 | |
| 	cutbuf.buf = (ZLE_STRING_T)zalloc(ZLE_CHAR_SIZE);
 | |
| 	cutbuf.buf[0] = ZWC('\0');
 | |
| 	cutbuf.len = cutbuf.flags = 0;
 | |
|     }
 | |
|     if (flags & (CUT_FRONT|CUT_REPLACE)) {
 | |
| 	ZLE_STRING_T s = (ZLE_STRING_T)zalloc((cutbuf.len + ct)*ZLE_CHAR_SIZE);
 | |
| 
 | |
| 	ZS_memcpy(s, line, ct);
 | |
| 	ZS_memcpy(s + ct, cutbuf.buf, cutbuf.len);
 | |
| 	free(cutbuf.buf);
 | |
| 	cutbuf.buf = s;
 | |
| 	cutbuf.len += ct;
 | |
|     } else {
 | |
| 	/* don't alloc 0 bytes; length 0 occurs for blank lines in vi mode */
 | |
| 	cutbuf.buf = realloc((char *)cutbuf.buf,
 | |
| 			     (cutbuf.len + (ct ? ct : 1)) * ZLE_CHAR_SIZE);
 | |
| 	ZS_memcpy(cutbuf.buf + cutbuf.len, line, ct);
 | |
| 	cutbuf.len += ct;
 | |
|     }
 | |
|     if(vilinerange)
 | |
| 	cutbuf.flags |= CUTBUFFER_LINE;
 | |
|     else
 | |
| 	cutbuf.flags &= ~CUTBUFFER_LINE;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Now we're back in the world of zlecs where we need to keep
 | |
|  * track of whether we're on a combining character.
 | |
|  */
 | |
| 
 | |
| /**/
 | |
| mod_export void
 | |
| backkill(int ct, int flags)
 | |
| {
 | |
|     UNMETACHECK();
 | |
|     if (flags & CUT_RAW) {
 | |
| 	zlecs -= ct;
 | |
|     } else {
 | |
| 	int origcs = zlecs;
 | |
| 	while (ct--)
 | |
| 	    DECCS();
 | |
| 	ct = origcs - zlecs;
 | |
|     }
 | |
| 
 | |
|     cut(zlecs, ct, flags);
 | |
|     shiftchars(zlecs, ct);
 | |
|     CCRIGHT();
 | |
| }
 | |
| 
 | |
| /**/
 | |
| mod_export void
 | |
| forekill(int ct, int flags)
 | |
| {
 | |
|     int i = zlecs;
 | |
| 
 | |
|     UNMETACHECK();
 | |
|     if (!(flags & CUT_RAW)) {
 | |
| 	int n = ct;
 | |
| 	while (n--)
 | |
| 	    INCCS();
 | |
| 	ct = zlecs - i;
 | |
| 	zlecs = i;
 | |
|     }
 | |
| 
 | |
|     cut(i, ct, flags);
 | |
|     shiftchars(i, ct);
 | |
|     CCRIGHT();
 | |
| }
 | |
| 
 | |
| /**/
 | |
| mod_export void
 | |
| backdel(int ct, int flags)
 | |
| {
 | |
|     if (flags & CUT_RAW) {
 | |
| 	if (zlemetaline != NULL) {
 | |
| 	    shiftchars(zlemetacs -= ct, ct);
 | |
| 	} else {
 | |
| 	    shiftchars(zlecs -= ct, ct);
 | |
| 	    CCRIGHT();
 | |
| 	}
 | |
|     } else {
 | |
| 	int n = ct, origcs = zlecs;
 | |
| 	DPUTS(zlemetaline != NULL, "backdel needs CUT_RAW when metafied");
 | |
| 	while (n--)
 | |
| 	    DECCS();
 | |
| 	shiftchars(zlecs, origcs - zlecs);
 | |
| 	CCRIGHT();
 | |
|     }
 | |
| }
 | |
| 
 | |
| /**/
 | |
| mod_export void
 | |
| foredel(int ct, int flags)
 | |
| {
 | |
|     if (flags & CUT_RAW) {
 | |
| 	if (zlemetaline != NULL) {
 | |
| 	    shiftchars(zlemetacs, ct);
 | |
| 	} else if (flags & CUT_RAW) {
 | |
| 	    shiftchars(zlecs, ct);
 | |
| 	    CCRIGHT();
 | |
| 	}
 | |
|     } else {
 | |
| 	int origcs = zlecs;
 | |
| 	int n = ct;
 | |
| 	DPUTS(zlemetaline != NULL, "foredel needs CUT_RAW when metafied");
 | |
| 	while (n--)
 | |
| 	    INCCS();
 | |
| 	ct = zlecs - origcs;
 | |
| 	zlecs = origcs;
 | |
| 	shiftchars(zlecs, ct);
 | |
| 	CCRIGHT();
 | |
|     }
 | |
| }
 | |
| 
 | |
| /**/
 | |
| void
 | |
| setline(char *s, int flags)
 | |
| {
 | |
|     char *scp;
 | |
| 
 | |
|     UNMETACHECK();
 | |
|     if (flags & ZSL_COPY)
 | |
| 	scp = ztrdup(s);
 | |
|     else
 | |
| 	scp = s;
 | |
|     /*
 | |
|      * TBD: we could make this more efficient by passing the existing
 | |
|      * allocated line to stringaszleline.
 | |
|      */
 | |
|     free(zleline);
 | |
| 
 | |
|     viinsbegin = 0;
 | |
|     zleline = stringaszleline(scp, 0, &zlell, &linesz, NULL);
 | |
| 
 | |
|     if ((flags & ZSL_TOEND) && (zlecs = zlell) && invicmdmode())
 | |
| 	DECCS();
 | |
|     else if (zlecs > zlell)
 | |
| 	zlecs = zlell;
 | |
|     CCRIGHT();
 | |
|     if (flags & ZSL_COPY)
 | |
| 	free(scp);
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| findbol(void)
 | |
| {
 | |
|     int x = zlecs;
 | |
| 
 | |
|     while (x > 0 && zleline[x - 1] != ZWC('\n'))
 | |
| 	x--;
 | |
|     return x;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| findeol(void)
 | |
| {
 | |
|     int x = zlecs;
 | |
| 
 | |
|     while (x != zlell && zleline[x] != ZWC('\n'))
 | |
| 	x++;
 | |
|     return x;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| void
 | |
| findline(int *a, int *b)
 | |
| {
 | |
|     *a = findbol();
 | |
|     *b = findeol();
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Query the user, and return 1 for yes, 0 for no.  The question is assumed to
 | |
|  * have been printed already, and the cursor is left immediately after the
 | |
|  * response echoed.  (Might cause a problem if this takes it onto the next
 | |
|  * line.)  <Tab> is interpreted as 'y'; any other control character is
 | |
|  * interpreted as 'n'.  If there are any characters in the buffer, this is
 | |
|  * taken as a negative response, and no characters are read.  Case is folded.
 | |
|  */
 | |
| 
 | |
| /**/
 | |
| mod_export int
 | |
| getzlequery(void)
 | |
| {
 | |
|     ZLE_INT_T c;
 | |
| #ifdef FIONREAD
 | |
|     int val;
 | |
| 
 | |
|     /* check for typeahead, which is treated as a negative response */
 | |
|     ioctl(SHTTY, FIONREAD, (char *)&val);
 | |
|     if (val) {
 | |
| 	putc('n', shout);
 | |
| 	return 0;
 | |
|     }
 | |
| #endif
 | |
| 
 | |
|     /* get a character from the tty and interpret it */
 | |
|     c = getfullchar(0);
 | |
|     /*
 | |
|      * We'll interpret an interruption here as only interrupting the
 | |
|      * query, not the line editor.
 | |
|      */
 | |
|     errflag &= ~ERRFLAG_INT;
 | |
|     if (c == ZWC('\t'))
 | |
| 	c = ZWC('y');
 | |
|     else if (ZC_icntrl(c) || c == ZLEEOF)
 | |
| 	c = ZWC('n');
 | |
|     else
 | |
| 	c = ZC_tolower(c);
 | |
|     /* echo response and return */
 | |
|     if (c != ZWC('\n')) {
 | |
| 	REFRESH_ELEMENT re;
 | |
| 	re.chr = c;
 | |
| 	re.atr = 0;
 | |
| 	zwcputc(&re, NULL);
 | |
|     }
 | |
|     return c == ZWC('y');
 | |
| }
 | |
| 
 | |
| /* Format a string, keybinding style. */
 | |
| 
 | |
| /**/
 | |
| char *
 | |
| bindztrdup(char *str)
 | |
| {
 | |
|     int c, len = 1;
 | |
|     char *buf, *ptr, *ret;
 | |
| 
 | |
|     for(ptr = str; *ptr; ptr++) {
 | |
| 	c = *ptr == Meta ? STOUC(*++ptr) ^ 32 : STOUC(*ptr);
 | |
| 	if(c & 0x80) {
 | |
| 	    len += 3;
 | |
| 	    c &= 0x7f;
 | |
| 	}
 | |
| 	if(c < 32 || c == 0x7f) {
 | |
| 	    len++;
 | |
| 	    c ^= 64;
 | |
| 	}
 | |
| 	len += c == '\\' || c == '^';
 | |
| 	len++;
 | |
|     }
 | |
|     ptr = buf = zalloc(len);
 | |
|     for(; *str; str++) {
 | |
| 	c = *str == Meta ? STOUC(*++str) ^ 32 : STOUC(*str);
 | |
| 	if(c & 0x80) {
 | |
| 	    *ptr++ = '\\';
 | |
| 	    *ptr++ = 'M';
 | |
| 	    *ptr++ = '-';
 | |
| 	    c &= 0x7f;
 | |
| 	}
 | |
| 	if(c < 32 || c == 0x7f) {
 | |
| 	    *ptr++ = '^';
 | |
| 	    c ^= 64;
 | |
| 	}
 | |
| 	if(c == '\\' || c == '^')
 | |
| 	    *ptr++ = '\\';
 | |
| 	*ptr++ = c;
 | |
|     }
 | |
|     *ptr = 0;
 | |
|     ret = dquotedztrdup(buf);
 | |
|     zsfree(buf);
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| /* Display a metafied string, keybinding-style. */
 | |
| 
 | |
| /**/
 | |
| int
 | |
| printbind(char *str, FILE *stream)
 | |
| {
 | |
|     char *b = bindztrdup(str);
 | |
|     int ret = zputs(b, stream);
 | |
| 
 | |
|     zsfree(b);
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Display a message where the completion list normally goes.
 | |
|  * The message must be metafied.
 | |
|  *
 | |
|  * TODO: there's some advantage in using a ZLE_STRING_T array here,
 | |
|  * together with improvements in other places, but messages don't
 | |
|  * need to be particularly efficient.
 | |
|  */
 | |
| 
 | |
| /**/
 | |
| mod_export void
 | |
| showmsg(char const *msg)
 | |
| {
 | |
|     char const *p;
 | |
|     int up = 0, cc = 0;
 | |
|     ZLE_CHAR_T c;
 | |
| #ifdef MULTIBYTE_SUPPORT
 | |
|     char *umsg;
 | |
|     int ulen, eol = 0;
 | |
|     size_t width;
 | |
|     mbstate_t mbs;
 | |
| #endif
 | |
| 
 | |
|     trashzle();
 | |
|     clearflag = isset(USEZLE) && !termflags && isset(ALWAYSLASTPROMPT);
 | |
| 
 | |
| #ifdef MULTIBYTE_SUPPORT
 | |
|     umsg = ztrdup(msg);
 | |
|     p = unmetafy(umsg, &ulen);
 | |
|     memset(&mbs, 0, sizeof mbs);
 | |
| 
 | |
|     mb_charinit();
 | |
|     while (ulen > 0) {
 | |
| 	char const *n;
 | |
| 	if (*p == '\n') {
 | |
| 	    ulen--;
 | |
| 	    p++;
 | |
| 
 | |
| 	    putc('\n', shout);
 | |
| 	    up += 1 + cc / zterm_columns;
 | |
| 	    cc = 0;
 | |
| 	} else {
 | |
| 	    /*
 | |
| 	     * Extract the next wide character from the multibyte string.
 | |
| 	     */
 | |
| 	    size_t cnt = eol ? MB_INVALID : mbrtowc(&c, p, ulen, &mbs);
 | |
| 
 | |
| 	    switch (cnt) {
 | |
| 	    case MB_INCOMPLETE:
 | |
| 		eol = 1;
 | |
| 		/* FALL THROUGH */
 | |
| 	    case MB_INVALID:
 | |
| 		/*
 | |
| 		 * This really shouldn't be happening here, but...
 | |
| 		 * Treat it as a single byte character; it may get
 | |
| 		 * prettified.
 | |
| 		 */
 | |
| 		memset(&mbs, 0, sizeof mbs);
 | |
| 		n = nicechar(*p);
 | |
| 		cnt = 1;
 | |
| 		width = strlen(n);
 | |
| 		break;
 | |
| 	    case 0:
 | |
| 		cnt = 1;
 | |
| 		/* FALL THROUGH */
 | |
| 	    default:
 | |
| 		/*
 | |
| 		 * Paranoia: only needed if we start in the middle
 | |
| 		 * of a multibyte string and only in some implementations.
 | |
| 		 */
 | |
| 		if (cnt > (size_t)ulen)
 | |
| 		    cnt = ulen;
 | |
| 		n = wcs_nicechar(c, &width, NULL);
 | |
| 		break;
 | |
| 	    }
 | |
| 	    ulen -= cnt;
 | |
| 	    p += cnt;
 | |
| 
 | |
| 	    zputs(n, shout);
 | |
| 	    cc += width;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|     free(umsg);
 | |
| #else
 | |
|     for(p = msg; (c = *p); p++) {
 | |
| 	if(c == Meta)
 | |
| 	    c = *++p ^ 32;
 | |
| 	if(c == '\n') {
 | |
| 	    putc('\n', shout);
 | |
| 	    up += 1 + cc / zterm_columns;
 | |
| 	    cc = 0;
 | |
| 	} else {
 | |
| 	    char const *n = nicechar(c);
 | |
| 	    zputs(n, shout);
 | |
| 	    cc += strlen(n);
 | |
| 	}
 | |
|     }
 | |
| #endif
 | |
|     up += cc / zterm_columns;
 | |
| 
 | |
|     if (clearflag) {
 | |
| 	putc('\r', shout);
 | |
| 	tcmultout(TCUP, TCMULTUP, up + nlnct);
 | |
|     } else
 | |
| 	putc('\n', shout);
 | |
|     showinglist = 0;
 | |
| }
 | |
| 
 | |
| /* handle the error flag */
 | |
| 
 | |
| /**/
 | |
| int
 | |
| handlefeep(UNUSED(char **args))
 | |
| {
 | |
|     zbeep();
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /* user control of auto-suffixes -- see iwidgets.list */
 | |
| 
 | |
| /**/
 | |
| int
 | |
| handlesuffix(UNUSED(char **args))
 | |
| {
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| /***************/
 | |
| /* undo system */
 | |
| /***************/
 | |
| 
 | |
| /* head of the undo list, and the current position */
 | |
| 
 | |
| /**/
 | |
| struct change *curchange;
 | |
| 
 | |
| static struct change *changes;
 | |
| 
 | |
| /* list of pending changes, not yet in the undo system */
 | |
| 
 | |
| static struct change *nextchanges, *endnextchanges;
 | |
| 
 | |
| /* incremented to provide a unique change number */
 | |
| 
 | |
| /**/
 | |
| zlong undo_changeno;
 | |
| 
 | |
| /* If positive, don't undo beyond this point */
 | |
| 
 | |
| static zlong undo_limitno;
 | |
| 
 | |
| /**/
 | |
| void
 | |
| initundo(void)
 | |
| {
 | |
|     nextchanges = NULL;
 | |
|     changes = curchange = zalloc(sizeof(*curchange));
 | |
|     curchange->prev = curchange->next = NULL;
 | |
|     curchange->del = curchange->ins = NULL;
 | |
|     curchange->dell = curchange->insl = 0;
 | |
|     curchange->changeno = undo_changeno = undo_limitno = 0;
 | |
|     lastline = zalloc((lastlinesz = linesz) * ZLE_CHAR_SIZE);
 | |
|     ZS_memcpy(lastline, zleline, (lastll = zlell));
 | |
|     lastcs = zlecs;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| void
 | |
| freeundo(void)
 | |
| {
 | |
|     freechanges(changes);
 | |
|     freechanges(nextchanges);
 | |
|     zfree(lastline, lastlinesz);
 | |
|     lastline = NULL;
 | |
|     lastlinesz = 0;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| static void
 | |
| freechanges(struct change *p)
 | |
| {
 | |
|     struct change *n;
 | |
| 
 | |
|     for(; p; p = n) {
 | |
| 	n = p->next;
 | |
| 	free(p->del);
 | |
| 	free(p->ins);
 | |
| 	zfree(p, sizeof(*p));
 | |
|     }
 | |
| }
 | |
| 
 | |
| /* register pending changes in the undo system */
 | |
| 
 | |
| /**/
 | |
| mod_export void
 | |
| handleundo(void)
 | |
| {
 | |
|     int remetafy;
 | |
| 
 | |
|     /*
 | |
|      * Yuk: we call this from within the completion system,
 | |
|      * so we need to convert back to the form which can be
 | |
|      * copied into undo entries.
 | |
|      */
 | |
|     if (zlemetaline != NULL) {
 | |
| 	unmetafy_line();
 | |
| 	remetafy = 1;
 | |
|     } else
 | |
| 	remetafy = 0;
 | |
| 
 | |
|     mkundoent();
 | |
|     if(nextchanges) {
 | |
| 	setlastline();
 | |
| 	if(curchange->next) {
 | |
| 	    freechanges(curchange->next);
 | |
| 	    curchange->next = NULL;
 | |
| 	    free(curchange->del);
 | |
| 	    free(curchange->ins);
 | |
| 	    curchange->del = curchange->ins = NULL;
 | |
| 	    curchange->dell = curchange->insl = 0;
 | |
| 	}
 | |
| 	nextchanges->prev = curchange->prev;
 | |
| 	if(curchange->prev)
 | |
| 	    curchange->prev->next = nextchanges;
 | |
| 	else
 | |
| 	    changes = nextchanges;
 | |
| 	curchange->prev = endnextchanges;
 | |
| 	endnextchanges->next = curchange;
 | |
| 	nextchanges = endnextchanges = NULL;
 | |
|     }
 | |
| 
 | |
|     if (remetafy)
 | |
| 	metafy_line();
 | |
| }
 | |
| 
 | |
| /* add an entry to the undo system, if anything has changed */
 | |
| 
 | |
| /**/
 | |
| void
 | |
| mkundoent(void)
 | |
| {
 | |
|     int pre, suf;
 | |
|     int sh = zlell < lastll ? zlell : lastll;
 | |
|     struct change *ch;
 | |
| 
 | |
|     UNMETACHECK();
 | |
|     if(lastll == zlell && !ZS_memcmp(lastline, zleline, zlell)) {
 | |
| 	lastcs = zlecs;
 | |
| 	return;
 | |
|     }
 | |
|     for(pre = 0; pre < sh && zleline[pre] == lastline[pre]; )
 | |
| 	pre++;
 | |
|     for(suf = 0; suf < sh - pre &&
 | |
| 	zleline[zlell - 1 - suf] == lastline[lastll - 1 - suf]; )
 | |
| 	suf++;
 | |
|     ch = zalloc(sizeof(*ch));
 | |
|     ch->next = NULL;
 | |
|     ch->hist = histline;
 | |
|     ch->off = pre;
 | |
|     ch->old_cs = lastcs;
 | |
|     ch->new_cs = zlecs;
 | |
|     if(suf + pre == lastll) {
 | |
| 	ch->del = NULL;
 | |
| 	ch->dell = 0;
 | |
|     } else {
 | |
| 	ch->dell = lastll - pre - suf;
 | |
| 	ch->del = (ZLE_STRING_T)zalloc(ch->dell * ZLE_CHAR_SIZE);
 | |
| 	ZS_memcpy(ch->del, lastline + pre, ch->dell);
 | |
|     }
 | |
|     if(suf + pre == zlell) {
 | |
| 	ch->ins = NULL;
 | |
| 	ch->insl = 0;
 | |
|     } else {
 | |
| 	ch->insl = zlell - pre - suf;
 | |
| 	ch->ins = (ZLE_STRING_T)zalloc(ch->insl * ZLE_CHAR_SIZE);
 | |
| 	ZS_memcpy(ch->ins, zleline + pre, ch->insl);
 | |
|     }
 | |
|     if(nextchanges) {
 | |
| 	ch->flags = CH_PREV;
 | |
| 	ch->prev = endnextchanges;
 | |
| 	endnextchanges->flags |= CH_NEXT;
 | |
| 	endnextchanges->next = ch;
 | |
|     } else {
 | |
| 	nextchanges = ch;
 | |
| 	ch->flags = 0;
 | |
| 	ch->prev = NULL;
 | |
|     }
 | |
|     ch->changeno = ++undo_changeno;
 | |
|     endnextchanges = ch;
 | |
| }
 | |
| 
 | |
| /* set lastline to match line */
 | |
| 
 | |
| /**/
 | |
| void
 | |
| setlastline(void)
 | |
| {
 | |
|     UNMETACHECK();
 | |
|     if(lastlinesz != linesz)
 | |
| 	lastline = realloc(lastline, (lastlinesz = linesz) * ZLE_CHAR_SIZE);
 | |
|     ZS_memcpy(lastline, zleline, (lastll = zlell));
 | |
|     lastcs = zlecs;
 | |
| }
 | |
| 
 | |
| /* move backwards through the change list */
 | |
| 
 | |
| /**/
 | |
| int
 | |
| undo(char **args)
 | |
| {
 | |
|     zlong last_change;
 | |
| 
 | |
|     if (*args)
 | |
| 	last_change = zstrtol(*args, NULL, 0);
 | |
|     else
 | |
| 	last_change = (zlong)-1;
 | |
| 
 | |
|     handleundo();
 | |
|     do {
 | |
| 	struct change *prev = curchange->prev;
 | |
| 	if(!prev)
 | |
| 	    return 1;
 | |
| 	if (prev->changeno <= last_change)
 | |
| 	    break;
 | |
| 	if (prev->changeno <= undo_limitno && !*args)
 | |
| 	    return 1;
 | |
| 	if (!unapplychange(prev)) {
 | |
| 	    if (last_change >= 0) {
 | |
| 		unapplychange(prev);
 | |
| 		curchange = prev;
 | |
| 	    }
 | |
| 	} else {
 | |
| 	    curchange = prev;
 | |
| 	}
 | |
|     } while (last_change >= (zlong)0 || (curchange->flags & CH_PREV));
 | |
|     setlastline();
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| static int
 | |
| unapplychange(struct change *ch)
 | |
| {
 | |
|     if(ch->hist != histline) {
 | |
| 	zle_setline(quietgethist(ch->hist));
 | |
| 	zlecs = ch->new_cs;
 | |
| 	return 0;
 | |
|     }
 | |
|     zlecs = ch->off;
 | |
|     if(ch->ins)
 | |
| 	foredel(ch->insl, CUT_RAW);
 | |
|     if(ch->del) {
 | |
| 	spaceinline(ch->dell);
 | |
| 	ZS_memcpy(zleline + zlecs, ch->del, ch->dell);
 | |
| 	zlecs += ch->dell;
 | |
|     }
 | |
|     zlecs = ch->old_cs;
 | |
|     return 1;
 | |
| }
 | |
| 
 | |
| /* move forwards through the change list */
 | |
| 
 | |
| /**/
 | |
| int
 | |
| redo(UNUSED(char **args))
 | |
| {
 | |
|     handleundo();
 | |
|     do {
 | |
| 	if(!curchange->next)
 | |
| 	    return 1;
 | |
| 	if (applychange(curchange))
 | |
| 	    curchange = curchange->next;
 | |
| 	else
 | |
| 	    break;
 | |
|     } while(curchange->prev->flags & CH_NEXT);
 | |
|     setlastline();
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| static int
 | |
| applychange(struct change *ch)
 | |
| {
 | |
|     if(ch->hist != histline) {
 | |
| 	zle_setline(quietgethist(ch->hist));
 | |
| 	zlecs = ch->old_cs;
 | |
| 	return 0;
 | |
|     }
 | |
|     zlecs = ch->off;
 | |
|     if(ch->del)
 | |
| 	foredel(ch->dell, CUT_RAW);
 | |
|     if(ch->ins) {
 | |
| 	spaceinline(ch->insl);
 | |
| 	ZS_memcpy(zleline + zlecs, ch->ins, ch->insl);
 | |
| 	zlecs += ch->insl;
 | |
|     }
 | |
|     zlecs = ch->new_cs;
 | |
|     return 1;
 | |
| }
 | |
| 
 | |
| /* vi undo: toggle between the end of the undo list and the preceding point */
 | |
| 
 | |
| /**/
 | |
| int
 | |
| viundochange(char **args)
 | |
| {
 | |
|     handleundo();
 | |
|     if(curchange->next) {
 | |
| 	do {
 | |
| 	    applychange(curchange);
 | |
| 	    curchange = curchange->next;
 | |
| 	} while(curchange->next);
 | |
| 	setlastline();
 | |
| 	return 0;
 | |
|     } else
 | |
| 	return undo(args);
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| splitundo(UNUSED(char **args))
 | |
| {
 | |
|     if (vistartchange >= 0) {
 | |
| 	mergeundo();
 | |
| 	vistartchange = undo_changeno;
 | |
|     }
 | |
|     handleundo();
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| void
 | |
| mergeundo(void)
 | |
| {
 | |
|     struct change *current;
 | |
|     for (current = curchange->prev;
 | |
| 	    current && current->prev && current->changeno > vistartchange+1;
 | |
| 	    current = current->prev) {
 | |
| 	current->flags |= CH_PREV;
 | |
| 	current->prev->flags |= CH_NEXT;
 | |
|     }
 | |
|     vistartchange = -1;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Call a ZLE hook: a user-defined widget called at a specific point
 | |
|  * within the line editor.
 | |
|  *
 | |
|  * A single argument arg is passed to the function (in addition to the
 | |
|  * function name).  It may be NULL.
 | |
|  */
 | |
| 
 | |
| /**/
 | |
| void
 | |
| zlecallhook(char *name, char *arg)
 | |
| {
 | |
|     Thingy thingy = rthingy_nocreate(name);
 | |
|     int saverrflag, savretflag;
 | |
|     char *args[2];
 | |
| 
 | |
|     if (!thingy)
 | |
| 	return;
 | |
| 
 | |
|     /* If anything here needs changing, see also redrawhook() */
 | |
| 
 | |
|     saverrflag = errflag;
 | |
|     savretflag = retflag;
 | |
| 
 | |
|     args[0] = arg;
 | |
|     args[1] = NULL;
 | |
|     execzlefunc(thingy, args, 1, 0);
 | |
|     unrefthingy(thingy);
 | |
| 
 | |
|     /* Retain any user interrupt error status */
 | |
|     errflag = saverrflag | (errflag & ERRFLAG_INT);
 | |
|     retflag = savretflag;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Return the number corresponding to the last change made.
 | |
|  */
 | |
| 
 | |
| /**/
 | |
| zlong
 | |
| get_undo_current_change(UNUSED(Param pm))
 | |
| {
 | |
|     int remetafy;
 | |
| 
 | |
|     /*
 | |
|      * Yuk: we call this from within the completion system,
 | |
|      * so we need to convert back to the form which can be
 | |
|      * copied into undo entries.
 | |
|      */
 | |
|     if (zlemetaline != NULL) {
 | |
| 	unmetafy_line();
 | |
| 	remetafy = 1;
 | |
|     } else
 | |
| 	remetafy = 0;
 | |
| 
 | |
|     /* add entry for any pending changes */
 | |
|     mkundoent();
 | |
|     setlastline();
 | |
| 
 | |
|     if (remetafy)
 | |
| 	metafy_line();
 | |
| 
 | |
|     return undo_changeno;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| zlong
 | |
| get_undo_limit_change(UNUSED(Param pm))
 | |
| {
 | |
|     return undo_limitno;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| void
 | |
| set_undo_limit_change(UNUSED(Param pm), zlong value)
 | |
| {
 | |
|     undo_limitno = value;
 | |
| }
 |