mirror of
git://git.code.sf.net/p/zsh/code
synced 2025-01-16 22:10:54 +01:00
cb5f100bd3
Save previous vi change and throw away a new change that fails. Add zle -f vichange to allow shell widget to be a single change. Fix repeat of command where numeric arguments were multiplied.
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);
|
|
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;
|
|
}
|