about summary refs log tree commit diff
path: root/Src/Zle/zle_utils.c
diff options
context:
space:
mode:
Diffstat (limited to 'Src/Zle/zle_utils.c')
-rw-r--r--Src/Zle/zle_utils.c650
1 files changed, 650 insertions, 0 deletions
diff --git a/Src/Zle/zle_utils.c b/Src/Zle/zle_utils.c
new file mode 100644
index 000000000..8fe3e7f0b
--- /dev/null
+++ b/Src/Zle/zle_utils.c
@@ -0,0 +1,650 @@
+/*
+ * 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[KRINGCT];
+/**/
+int kringnum;
+
+/* Vi named cut buffers.  0-25 are the named buffers "a to "z, and *
+ * 26-34 are the numbered buffer stack "1 to "9.                   */
+
+/**/
+struct cutbuffer vibuf[35];
+
+/* the line before last mod (for undo purposes) */
+
+/**/
+char *lastline;
+/**/
+int lastlinesz, lastll;
+
+/* size of line buffer */
+
+/**/
+int linesz;
+
+/* make sure that the line buffer has at least sz chars */
+
+/**/
+void
+sizeline(int sz)
+{
+    while (sz > linesz)
+	line = (unsigned char *)realloc(line, (linesz *= 4) + 2);
+}
+
+/* insert space for ct chars at cursor position */
+
+/**/
+void
+spaceinline(int ct)
+{
+    int i;
+
+    sizeline(ct + ll);
+    for (i = ll; --i >= cs;)
+	line[i + ct] = line[i];
+    ll += ct;
+    line[ll] = '\0';
+
+    if (mark > cs)
+	mark += ct;
+}
+
+/**/
+static void
+shiftchars(int to, int cnt)
+{
+    if (mark >= to + cnt)
+	mark -= cnt;
+    else if (mark > to)
+	mark = to;
+
+    while (to + cnt < ll) {
+	line[to] = line[to + cnt];
+	to++;
+    }
+    line[ll = to] = '\0';
+}
+
+/**/
+void
+backkill(int ct, int dir)
+{
+    int i = (cs -= ct);
+
+    cut(i, ct, dir);
+    shiftchars(i, ct);
+}
+
+/**/
+void
+forekill(int ct, int dir)
+{
+    int i = cs;
+
+    cut(i, ct, dir);
+    shiftchars(i, ct);
+}
+
+/**/
+void
+cut(int i, int ct, int dir)
+{
+    if (zmod.flags & MOD_VIBUF) {
+	struct cutbuffer *b = &vibuf[zmod.vibuf];
+
+	if (!(zmod.flags & MOD_VIAPP) || !b->buf) {
+	    zfree(b->buf, b->len);
+	    b->buf = (char *)zalloc(ct);
+	    memcpy(b->buf, (char *) line + i, ct);
+	    b->len = ct;
+	    b->flags = vilinerange ? CUTBUFFER_LINE : 0;
+	} else {
+	    int len = b->len;
+
+	    if(vilinerange)
+		b->flags |= CUTBUFFER_LINE;
+	    b->buf = realloc(b->buf, ct + len + !!(b->flags & CUTBUFFER_LINE));
+	    if (b->flags & CUTBUFFER_LINE)
+		b->buf[len++] = '\n';
+	    memcpy(b->buf + len, (char *) line + i, ct);
+	    b->len = len + ct;
+	}
+	return;
+    } else {
+	/* Save in "1, shifting "1-"8 along to "2-"9 */
+	int n;
+	zfree(vibuf[34].buf, vibuf[34].len);
+	for(n=34; n>26; n--)
+	    vibuf[n] = vibuf[n-1];
+	vibuf[26].buf = (char *)zalloc(ct);
+	memcpy(vibuf[26].buf, (char *) line + i, ct);
+	vibuf[26].len = ct;
+	vibuf[26].flags = vilinerange ? CUTBUFFER_LINE : 0;
+    }
+    if (!cutbuf.buf) {
+	cutbuf.buf = ztrdup("");
+	cutbuf.len = cutbuf.flags = 0;
+    } else if (!(lastcmd & ZLE_KILL)) {
+	kringnum = (kringnum + 1) % KRINGCT;
+	if (kring[kringnum].buf)
+	    free(kring[kringnum].buf);
+	kring[kringnum] = cutbuf;
+	cutbuf.buf = ztrdup("");
+	cutbuf.len = cutbuf.flags = 0;
+    }
+    if (dir) {
+	char *s = (char *)zalloc(cutbuf.len + ct);
+
+	memcpy(s, (char *) line + i, ct);
+	memcpy(s + ct, cutbuf.buf, cutbuf.len);
+	free(cutbuf.buf);
+	cutbuf.buf = s;
+	cutbuf.len += ct;
+    } else {
+	cutbuf.buf = realloc(cutbuf.buf, cutbuf.len + ct);
+	memcpy(cutbuf.buf + cutbuf.len, (char *) line + i, ct);
+	cutbuf.len += ct;
+    }
+    if(vilinerange)
+	cutbuf.flags |= CUTBUFFER_LINE;
+    else
+	cutbuf.flags &= ~CUTBUFFER_LINE;
+}
+
+/**/
+void
+backdel(int ct)
+{
+    shiftchars(cs -= ct, ct);
+}
+
+/**/
+void
+foredel(int ct)
+{
+    shiftchars(cs, ct);
+}
+
+/**/
+void
+setline(char const *s)
+{
+    sizeline(strlen(s));
+    strcpy((char *) line, s);
+    unmetafy((char *) line, &ll);
+    if ((cs = ll) && invicmdmode())
+	cs--;
+}
+
+/**/
+int
+findbol(void)
+{
+    int x = cs;
+
+    while (x > 0 && line[x - 1] != '\n')
+	x--;
+    return x;
+}
+
+/**/
+int
+findeol(void)
+{
+    int x = cs;
+
+    while (x != ll && line[x] != '\n')
+	x++;
+    return x;
+}
+
+/**/
+void
+findline(int *a, int *b)
+{
+    *a = findbol();
+    *b = findeol();
+}
+
+/* Search for needle in haystack.  Haystack is a metafied string while *
+ * needle is unmetafied and len-long.  Start the search at position    *
+ * pos.  Search forward if dir > 0 otherwise search backward.          */
+
+/**/
+char *
+hstrnstr(char *haystack, int pos, char *needle, int len, int dir, int sens)
+{
+    char *s = haystack + pos;
+
+    if (dir > 0) {
+	while (*s) {
+	    if (metadiffer(s, needle, len) < sens)
+		return s;
+	    s += 1 + (*s == Meta);
+	}
+    } else {
+	for (;;) {
+	    if (metadiffer(s, needle, len) < sens)
+		return s;
+	    if (s == haystack)
+		break;
+	    s -= 1 + (s != haystack+1 && s[-2] == Meta);
+	}
+    }
+    return NULL;
+}
+
+/* Query the user, and return a single character response.  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.                        */
+
+/**/
+int
+getzlequery(void)
+{
+    int 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 'n';
+    }
+#endif
+
+    /* get a character from the tty and interpret it */
+    c = getkey(0);
+    if (c == '\t')
+	c = 'y';
+    else if (icntrl(c) || c == EOF)
+	c = 'n';
+    else
+	c = tulower(c);
+
+    /* echo response and return */
+    putc(c, shout);
+    return c;
+}
+
+/* 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.                              */
+
+/**/
+void
+showmsg(char const *msg)
+{
+    char const *p;
+    int up = 0, cc = 0, c;
+
+    trashzle();
+    clearflag = isset(USEZLE) && !termflags && isset(ALWAYSLASTPROMPT);
+
+    for(p = msg; (c = *p); p++) {
+	if(c == Meta)
+	    c = *++p ^ 32;
+	if(c == '\n') {
+	    putc('\n', shout);
+	    up += 1 + cc / columns;
+	    cc = 0;
+	} else {
+	    char const *n = nicechar(c);
+	    fputs(n, shout);
+	    cc += strlen(n);
+	}
+    }
+    up += cc / columns;
+
+    if (clearflag) {
+	putc('\r', shout);
+	tcmultout(TCUP, TCMULTUP, up + nlnct);
+    } else
+	putc('\n', shout);
+    showinglist = 0;
+}
+
+/* handle the error flag */
+
+/**/
+void
+feep(void)
+{
+    feepflag = 1;
+}
+
+/**/
+void
+handlefeep(void)
+{
+    if(feepflag)
+	beep();
+    feepflag = 0;
+}
+
+/***************/
+/* undo system */
+/***************/
+
+/* head of the undo list, and the current position */
+
+static struct change *changes, *curchange;
+
+/* list of pending changes, not yet in the undo system */
+
+static struct change *nextchanges, *endnextchanges;
+
+/**/
+void
+initundo(void)
+{
+    nextchanges = NULL;
+    changes = curchange = zalloc(sizeof(*curchange));
+    curchange->prev = curchange->next = NULL;
+    curchange->del = curchange->ins = NULL;
+    lastline = zalloc(lastlinesz = linesz);
+    memcpy(lastline, line, lastll = ll);
+}
+
+/**/
+void
+freeundo(void)
+{
+    freechanges(changes);
+    freechanges(nextchanges);
+    zfree(lastline, lastlinesz);
+}
+
+/**/
+static void
+freechanges(struct change *p)
+{
+    struct change *n;
+
+    for(; p; p = n) {
+	n = p->next;
+	zsfree(p->del);
+	zsfree(p->ins);
+	zfree(p, sizeof(*p));
+    }
+}
+
+/* register pending changes in the undo system */
+
+/**/
+void
+handleundo(void)
+{
+    mkundoent();
+    if(!nextchanges)
+	return;
+    setlastline();
+    if(curchange->next) {
+	freechanges(curchange->next);
+	curchange->next = NULL;
+	zsfree(curchange->del);
+	zsfree(curchange->ins);
+	curchange->del = curchange->ins = NULL;
+    }
+    nextchanges->prev = curchange->prev;
+    if(curchange->prev)
+	curchange->prev->next = nextchanges;
+    else
+	changes = nextchanges;
+    curchange->prev = endnextchanges;
+    endnextchanges->next = curchange;
+    nextchanges = endnextchanges = NULL;
+}
+
+/* add an entry to the undo system, if anything has changed */
+
+/**/
+void
+mkundoent(void)
+{
+    int pre, suf;
+    int sh = ll < lastll ? ll : lastll;
+    struct change *ch;
+
+    if(lastll == ll && !memcmp(lastline, line, ll))
+	return;
+    for(pre = 0; pre < sh && line[pre] == lastline[pre]; )
+	pre++;
+    for(suf = 0; suf < sh - pre &&
+	line[ll - 1 - suf] == lastline[lastll - 1 - suf]; )
+	suf++;
+    ch = zalloc(sizeof(*ch));
+    ch->next = NULL;
+    ch->hist = histline;
+    ch->off = pre;
+    if(suf + pre == lastll)
+	ch->del = NULL;
+    else
+	ch->del = metafy(lastline + pre, lastll - pre - suf, META_DUP);
+    if(suf + pre == ll)
+	ch->ins = NULL;
+    else
+	ch->ins = metafy((char *)line + pre, ll - pre - suf, META_DUP);
+    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;
+    }
+    endnextchanges = ch;
+}
+
+/* set lastline to match line */
+
+/**/
+void
+setlastline(void)
+{
+    if(lastlinesz != linesz)
+	lastline = realloc(lastline, lastlinesz = linesz);
+    memcpy(lastline, line, lastll = ll);
+}
+
+/* move backwards through the change list */
+
+/**/
+void
+undo(void)
+{
+    handleundo();
+    do {
+	if(!curchange->prev) {
+	    feep();
+	    return;
+	}
+	unapplychange(curchange = curchange->prev);
+    } while(curchange->flags & CH_PREV);
+    setlastline();
+}
+
+/**/
+static void
+unapplychange(struct change *ch)
+{
+    if(ch->hist != histline) {
+	remember_edits();
+	setline(zle_get_event(histline = ch->hist));
+    }
+    cs = ch->off;
+    if(ch->ins)
+	foredel(ztrlen(ch->ins));
+    if(ch->del) {
+	char *c = ch->del;
+
+	spaceinline(ztrlen(c));
+	for(; *c; c++)
+	    if(*c == Meta)
+		line[cs++] = STOUC(*++c) ^ 32;
+	    else
+		line[cs++] = STOUC(*c);
+    }
+}
+
+/* move forwards through the change list */
+
+/**/
+void
+redo(void)
+{
+    handleundo();
+    do {
+	if(!curchange->next) {
+	    feep();
+	    return;
+	}
+	applychange(curchange);
+	curchange = curchange->next;
+    } while(curchange->prev->flags & CH_NEXT);
+    setlastline();
+}
+
+/**/
+static void
+applychange(struct change *ch)
+{
+    if(ch->hist != histline) {
+	remember_edits();
+	setline(zle_get_event(histline = ch->hist));
+    }
+    cs = ch->off;
+    if(ch->del)
+	foredel(ztrlen(ch->del));
+    if(ch->ins) {
+	char *c = ch->ins;
+
+	spaceinline(ztrlen(c));
+	for(; *c; c++)
+	    if(*c == Meta)
+		line[cs++] = STOUC(*++c) ^ 32;
+	    else
+		line[cs++] = STOUC(*c);
+    }
+}
+
+/* vi undo: toggle between the end of the undo list and the preceding point */
+
+/**/
+void
+viundochange(void)
+{
+    handleundo();
+    if(curchange->next) {
+	do {
+	    applychange(curchange);
+	    curchange = curchange->next;
+	} while(curchange->next);
+	setlastline();
+    } else
+	undo();
+}