/*
 * $Id: zle_hist.c,v 1.8 1995/04/25 22:18:18 coleman Exp coleman $
 *
 * zle_hist.c - history editing
 *
 * This file is part of zsh, the Z shell.
 *
 * Copyright (c) 1992-1995 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 its documentation 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.
 *
 */

#define ZLE
#include "zsh.h"

/**/
void
toggleliteralhistory(void)
{
    char *s;

    if (histline == curhist) {
	zsfree(curhistline);
	curhistline = ztrdup(UTOSCP(line));
    }
    lithist ^= 1;
    if (!(s = qgetevent(histline)))
	feep();
    else
	sethistline(STOUCP(s));
}

/**/
void
uphistory(void)
{
    char *s;

    if (mult < 0) {
	mult = -mult;
	downhistory();
	return;
    }
    if (histline == curhist) {
	zsfree(curhistline);
	curhistline = ztrdup(UTOSCP(line));
    }
    histline -= mult;
    if (!(s = qgetevent(histline))) {
	if (unset(NOHISTBEEP))
	    feep();
	histline += mult;
    } else
	sethistline(STOUCP(s));
}

/**/
void
uplineorhistory(void)
{
    int ocs = cs;

    if (mult < 0) {
	mult = -mult;
	downlineorhistory();
	return;
    }
    if ((lastcmd & ZLE_LINEMOVE) != ZLE_LINEMOVE)
	lastcol = cs - findbol();
    cs = findbol();
    while (mult) {
	if (!cs)
	    break;
	cs--;
	cs = findbol();
	mult--;
    }
    if (mult) {
	cs = ocs;
	if (virangeflag) {
	    feep();
	    return;
	}
	uphistory();
    } else {
	int x = findeol();

	if ((cs += lastcol) >= x) {
	    cs = x;
	    if (cs > findbol() && bindtab == altbindtab)
		cs--;
	}
    }
}

/**/
void
viuplineorhistory(void)
{
    int ocs = cs;

    if (mult < 0) {
	mult = -mult;
	downlineorhistory();
	return;
    }
    cs = findbol();
    while (mult) {
	if (!cs)
	    break;
	cs--;
	cs = findbol();
	mult--;
    }
    if (mult) {
	cs = ocs;
	if (virangeflag) {
	    feep();
	    return;
	}
	uphistory();
    }
    vifirstnonblank();
    lastcol = cs - findbol();
}


/**/
void
uplineorsearch(void)
{
    int ocs = cs;

    if (mult < 0) {
	mult = -mult;
	downlineorsearch();
	return;
    }
    if ((lastcmd & ZLE_LINEMOVE) != ZLE_LINEMOVE)
	lastcol = cs - findbol();
    cs = findbol();
    while (mult) {
	if (!cs)
	    break;
	cs--;
	cs = findbol();
	mult--;
    }
    if (mult) {
	cs = ocs;
	if (virangeflag) {
	    feep();
	    return;
	}
	historysearchbackward();
    } else {
	int x = findeol();

	if ((cs += lastcol) >= x) {
	    cs = x;
	    if (cs && bindtab == altbindtab)
		cs--;
	}
    }
}

/**/
void
downlineorhistory(void)
{
    int ocs = cs;

    if (mult < 0) {
	mult = -mult;
	uplineorhistory();
	return;
    }
    if ((lastcmd & ZLE_LINEMOVE) != ZLE_LINEMOVE)
	lastcol = cs - findbol();
    while (mult) {
	int x = findeol();

	if (x == ll)
	    break;
	cs = x + 1;
	mult--;
    }
    if (mult) {
	cs = ocs;
	if (virangeflag) {
	    feep();
	    return;
	}
	downhistory();
    } else {
	int x = findeol();

	if ((cs += lastcol) >= x) {
	    cs = x;
	    if (cs > findbol() && bindtab == altbindtab)
		cs--;
	}
    }
}

/**/
void
vidownlineorhistory(void)
{
    int ocs = cs;

    if (mult < 0) {
	mult = -mult;
	uplineorhistory();
	return;
    }
    while (mult) {
	int x = findeol();

	if (x == ll)
	    break;
	cs = x + 1;
	mult--;
    }
    if (mult) {
	cs = ocs;
	if (virangeflag) {
	    feep();
	    return;
	}
	downhistory();
    }
    vifirstnonblank();
    lastcol = cs - findbol();
}

/**/
void
downlineorsearch(void)
{
    int ocs = cs;

    if (mult < 0) {
	mult = -mult;
	uplineorsearch();
	return;
    }
    if ((lastcmd & ZLE_LINEMOVE) != ZLE_LINEMOVE)
	lastcol = cs - findbol();
    while (mult) {
	int x = findeol();

	if (x == ll)
	    break;
	cs = x + 1;
	mult--;
    }
    if (mult) {
	cs = ocs;
	if (virangeflag) {
	    feep();
	    return;
	}
	historysearchforward();
    } else {
	int x = findeol();

	if ((cs += lastcol) >= x) {
	    cs = x;
	    if (cs && bindtab == altbindtab)
		cs--;
	}
    }
}

/**/
void
acceptlineanddownhistory(void)
{
    char *s, *t;

    if (!(s = qgetevent(histline + 1))) {
	feep();
	return;
    }
    pushnode(bufstack, t = ztrdup(s));
    for (; *t; t++)
	if (*t == HISTSPACE)
	    *t = ' ';
    done = 1;
    stackhist = histline + 1;
}

/**/
void
downhistory(void)
{
    char *s;

    if (mult < 0) {
	mult = -mult;
	uphistory();
	return;
    }
    histline += mult;
    if (!(s = qgetevent(histline))) {
	if (unset(NOHISTBEEP))
	    feep();
	histline -= mult;
	return;
    }
    sethistline(STOUCP(s));
}

static int histpos;

/**/
void
historysearchbackward(void)
{
    int t0, ohistline = histline;
    char *s;

    if (histline == curhist) {
	zsfree(curhistline);
	curhistline = ztrdup(UTOSCP(line));
    }
    if (lastcmd & ZLE_HISTSEARCH)
	t0 = histpos;
    else
	for (t0 = 0; line[t0] && !iblank(line[t0]); t0++);
    histpos = t0;
    for (;;) {
	histline--;
	if (!(s = qgetevent(histline))) {
	    feep();
	    histline = ohistline;
	    return;
	}
	if (!hstrncmp(s, UTOSCP(line), t0) && hstrcmp(s, UTOSCP(line)))
	    break;
    }
    sethistline(STOUCP(s));
}

/**/
void
historysearchforward(void)
{
    int t0, ohistline = histline;
    char *s;

    if (histline == curhist) {
	zsfree(curhistline);
	curhistline = ztrdup(UTOSCP(line));
    }
    if (lastcmd & ZLE_HISTSEARCH)
	t0 = histpos;
    else
	for (t0 = 0; line[t0] && !iblank(line[t0]); t0++);
    histpos = t0;
    for (;;) {
	histline++;
	if (!(s = qgetevent(histline))) {
	    feep();
	    histline = ohistline;
	    return;
	}
	if (!hstrncmp(s, UTOSCP(line), t0) && hstrcmp(s, UTOSCP(line)))
	    break;
    }
    sethistline(STOUCP(s));
}

/**/
void
beginningofbufferorhistory(void)
{
    if (findbol())
	cs = 0;
    else
	beginningofhistory();
}

/**/
void
beginningofhistory(void)
{
    char *s;

    if (histline == curhist) {
	zsfree(curhistline);
	curhistline = ztrdup(UTOSCP(line));
    }
    if (!(s = qgetevent(firsthist()))) {
	if (unset(NOHISTBEEP))
	    feep();
	return;
    }
    histline = firsthist();
    sethistline(STOUCP(s));
}

/**/
void
endofbufferorhistory(void)
{
    if (findeol() != ll)
	cs = ll;
    else
	endofhistory();
}

/**/
void
endofhistory(void)
{
    if (histline == curhist) {
	if (unset(NOHISTBEEP))
	    feep();
    } else {
	histline = curhist;
	sethistline(STOUCP(curhistline));
    }
}

/**/
void
insertlastword(void)
{
    char *s, *t;
    int len, z = lithist;

/* multiple calls will now search back through the history, pem */
    static char *lastinsert;
    static int lasthist, lastpos;
    int evhist = curhist - 1;

    if (lastinsert) {
	int lastlen = strlen(lastinsert);
	int pos = cs;

	if (lastpos <= pos &&
	    lastlen == pos - lastpos &&
	    strncmp(lastinsert, (char *)&line[lastpos], lastlen) == 0) {
	    evhist = --lasthist;
	    cs = lastpos;
	    foredel(pos - cs);
	}
	zsfree(lastinsert);
	lastinsert = NULL;
    }
    lithist = 0;
    if (!(s = qgetevent(evhist), lithist = z, s)) {
	feep();
	return;
    }
    for (t = s + strlen(s); t > s; t--)
	if (*t == HISTSPACE)
	    break;
    if (t != s)
	t++;
    lasthist = evhist;
    lastpos = cs;
    lastinsert = ztrdup(t);
    spaceinline(len = strlen(t));
    strncpy((char *)line + cs, t, len);
    cs += len;
}

/**/
char *
qgetevent(int ev)
{
    if (ev > curhist)
	return NULL;
    return ((ev == curhist) ? curhistline : quietgetevent(ev));
}

/**/
void
pushline(void)
{
    if (mult < 0)
	return;
    pushnode(bufstack, ztrdup(UTOSCP(line)));
    while (--mult)
	pushnode(bufstack, ztrdup(""));
    stackcs = cs;
    *line = '\0';
    ll = cs = 0;
}

/**/
void
pushpopinput(void)
{
    int ics;
    char *iline;
    Histent curhistent = gethistent(curhist);

    if (mult < 0)
	return;
    if (*(curhistent->lit)) {
	ics = strlen(curhistent->lit);
	iline = (char *)zalloc(strlen((char *)line) + ics + 1);
	strcpy(iline, curhistent->lit);
	strcat(iline, (char *)line);
	free(line);
	line = (unsigned char *)iline;
	ll += ics;
	cs += ics;
	*(curhistent->lit) = '\0';
    }
    pushline();
    if (!isfirstln) {
	*(hptr = chline) = '\0';
	errflag = done = 1;
    }
}

/**/
void
pushinput(void)
{
    if (mult < 0)
	return;
    if (!isfirstln)
	mult++;
    pushpopinput();
}

/**/
void
getline(void)
{
    char *s = (char *)getnode(bufstack);

    if (!s)
	feep();
    else {
	int cc;

	cc = strlen(s);
	spaceinline(cc);
	strncpy((char *)line + cs, s, cc);
	cs += cc;
	zsfree(s);
    }
}

/**/
void
historyincrementalsearchbackward(void)
{
    doisearch(-1);
}

/**/
void
historyincrementalsearchforward(void)
{
    doisearch(1);
}

extern int ungetok;

/**/
void
doisearch(int dir)
{
    char *s, *oldl;
    char ibuf[256], *sbuf = ibuf + 14;
    int sbptr = 0, cmd, ohl = histline, ocs = cs;
    int nomatch, chequiv = 0;

    strcpy(ibuf, (dir == -1) ? "bck-i-search: " : "fwd-i-search: ");
    statusline = ibuf;
    oldl = ztrdup(UTOSCP(line));
    if (histline == curhist) {
	zsfree(curhistline);
	curhistline = ztrdup(UTOSCP(line));
    }
    bindtab = mainbindtab;
    for (;;) {
	nomatch = 0;
	if (sbptr > 1 || (sbptr == 1 && sbuf[0] != '^')) {
	    int ohistline = histline;

	    for (;;) {
		char *t;

		if (!(s = qgetevent(histline))) {
		    feep();
		    nomatch = 1;
		    histline = ohistline;
		    break;
		}
		if ((sbuf[0] == '^') ?
		    (t = (hstrncmp(s, sbuf + 1, sbptr - 1)) ? NULL : s) :
		    (t = hstrnstr(s, sbuf, sbptr)))
		    if (!(chequiv && !hstrcmp(UTOSCP(line), s))) {
			sethistline(STOUCP(s));
			cs = t - s + sbptr - (sbuf[0] == '^');
			break;
		    }
		histline += dir;
	    }
	    chequiv = 0;
	}
	refresh();
	if ((cmd = getkeycmd()) < 0 || cmd == z_sendbreak) {
	    setline(oldl);
	    cs = ocs;
	    histline = ohl;
	    break;
	}
	switch (cmd) {
	case z_vicmdmode:
	    bindtab = (bindtab == mainbindtab) ? altbindtab : mainbindtab;
	    continue;
	case z_vibackwarddeletechar:
	case z_backwarddeletechar:
	    if (sbptr)
		sbuf[--sbptr] = '\0';
	    else
		feep();
	    histline = ohl;
	    continue;
	case z_acceptandhold:
	    acceptandhold();
	    goto brk;
	case z_acceptandinfernexthistory:
	    acceptandinfernexthistory();
	    goto brk;
	case z_acceptlineanddownhistory:
	    acceptlineanddownhistory();
	    goto brk;
	case z_acceptline:
	    acceptline();
	    goto brk;
	case z_virevrepeatsearch:
	case z_historyincrementalsearchbackward:
	    ohl = (histline += (dir = -1));
	    chequiv = 1;
	    memcpy(ibuf, "bck", 3);
	    refresh();
	    continue;
	case z_virepeatsearch:
	case z_historyincrementalsearchforward:
	    ohl = (histline += (dir = 1));
	    chequiv = 1;
	    memcpy(ibuf, "fwd", 3);
	    refresh();
	    continue;
	case z_sendstring:
	    sendstring();
	    continue;
	case z_quotedinsert:
	    if ((c = getkey(0)) == EOF)
		goto brk;
	    else
		cmd = z_selfinsert;
	default:
	    if (cmd == z_magicspace)
		c = ' ';
	    else if (cmd != z_selfinsert && cmd != z_selfinsertunmeta) {
		if (ungetok)
		    ungetkey(c);
		else
		    feep();
		goto brk;
	    }
	    if (!nomatch && sbptr != 39) {
		sbuf[sbptr++] = c;
		sbuf[sbptr] = '\0';
	    }
	}
    }
  brk:
    free(oldl);
    statusline = NULL;
}

/**/
void
acceptandinfernexthistory(void)
{
    int t0;
    char *s, *t;

    done = 1;
    for (t0 = histline - 2;; t0--) {
	if (!(s = qgetevent(t0)))
	    return;
	if (!hstrncmp(s, UTOSCP(line), ll))
	    break;
    }
    if (!(s = qgetevent(t0 + 1)))
	return;
    pushnode(bufstack, t = ztrdup(s));
    for (; *t; t++)
	if (*t == HISTSPACE)
	    *t = ' ';
    stackhist = t0 + 1;
}

/**/
void
infernexthistory(void)
{
    int t0;
    char *s, *t;

    if (!(t = qgetevent(histline - 1))) {
	feep();
	return;
    }
    for (t0 = histline - 2;; t0--) {
	if (!(s = qgetevent(t0))) {
	    feep();
	    return;
	}
	if (!strcmp(s, t))
	    break;
    }
    if (!(s = qgetevent(t0 + 1))) {
	feep();
	return;
    }
    histline = t0 + 1;
    sethistline(STOUCP(s));
}

/**/
void
vifetchhistory(void)
{
    char *s;

    if (mult < 0)
	return;
    if (histline == curhist) {
	if (!gotmult) {
	    cs = ll;
	    cs = findbol();
	    return;
	}
	zsfree(curhistline);
	curhistline = ztrdup(UTOSCP(line));
    }
    if (!gotmult)
	mult = curhist;
    if (!(s = qgetevent(mult)))
	feep();
    else {
	histline = mult;
	sethistline(STOUCP(s));
    }
}

extern int viins_cur_bindtab[];

/**/
int
getvisrchstr(void)
{
    static char sbuf[80] = "/";
    int sptr = 1, cmd, ret = 0;
    int *obindtab = NULL;

    if (visrchstr) {
	zsfree(visrchstr);
	visrchstr = NULL;
    }
    statusline = sbuf;
    sbuf[1] = '\0';
    if (bindtab == altbindtab) {
	obindtab = bindtab;
	bindtab = viins_cur_bindtab;
    }
    while (sptr) {
	refresh();
	if ((cmd = getkeycmd()) < 0 || cmd == z_sendbreak) {
	    ret = 0;
	    break;
	} else if (cmd == z_acceptline || cmd == z_vicmdmode) {
	    visrchstr = ztrdup(sbuf + 1);
	    ret = 1;
	    break;
	} else if (cmd == z_backwarddeletechar ||
		   cmd == z_vibackwarddeletechar) {
	    sbuf[--sptr] = '\0';
	    continue;
	} else if (cmd == z_sendstring) {
	    sendstring();
	    continue;
	} else if (cmd == z_quotedinsert) {
	    if ((c = getkey(0)) == EOF) {
		feep();
		continue;
	    }
	} else if (cmd != z_selfinsert && cmd != z_selfinsertunmeta) {
	    feep();
	    continue;
	}
	if (sptr != 79) {
	    sbuf[sptr++] = c;
	    sbuf[sptr] = '\0';
	}
    }
    statusline = NULL;
    if (obindtab)
	bindtab = obindtab;
    return ret;
}

/**/
void
vihistorysearchforward(void)
{
    visrchsense = 1;
    if (getvisrchstr())
	virepeatsearch();
}

/**/
void
vihistorysearchbackward(void)
{
    visrchsense = -1;
    if (getvisrchstr())
	virepeatsearch();
}

/**/
void
virepeatsearch(void)
{
    int ohistline = histline, t0;
    char *s;

    if (!visrchstr) {
	feep();
	return;
    }
    t0 = strlen(visrchstr);
    if (histline == curhist) {
	zsfree(curhistline);
	curhistline = ztrdup(UTOSCP(line));
    }
    for (;;) {
	histline += visrchsense;
	if (!(s = qgetevent(histline))) {
	    feep();
	    histline = ohistline;
	    return;
	}
	if (!hstrcmp(UTOSCP(line), s))
	    continue;
	if (*visrchstr == '^') {
	    if (!hstrncmp(s, visrchstr + 1, t0 - 1))
		break;
	} else if (hstrnstr(s, visrchstr, t0))
	    break;
    }
    sethistline(STOUCP(s));
}

/**/
void
virevrepeatsearch(void)
{
    visrchsense = -visrchsense;
    virepeatsearch();
    visrchsense = -visrchsense;
}

/* Extra function added by A.R. Iano-Fletcher.	*/
/*The extern variable "cs" is the position of the cursor. */
/* history-beginning-search-backward */

/**/
void
historybeginningsearchbackward(void)
{
    int cpos = cs;		/* save cursor position */
    int ohistline = histline;
    char *s;

    if (histline == curhist) {
	zsfree(curhistline);
	curhistline = ztrdup((char *)line);
    }
    for (;;) {
	histline--;
	if (!(s = qgetevent(histline))) {
	    feep();
	    histline = ohistline;
	    return;
	}
	if (!hstrncmp(s, (char *)line, cs) && hstrcmp(s, (char *)line))
	    break;
    }

    sethistline((unsigned char *)s);	/* update command line.		*/
    cs = cpos;			/* reset cursor position.	*/
}

/* Extra function added by A.R. Iano-Fletcher.	*/

/* history-beginning-search-forward */
/**/
void
historybeginningsearchforward(void)
{
    int cpos = cs;		/* save cursor position */
    int ohistline = histline;
    char *s;

    if (histline == curhist) {
	zsfree(curhistline);
	curhistline = ztrdup((char *)line);
    }
    for (;;) {
	histline++;
	if (!(s = qgetevent(histline))) {
	    feep();
	    histline = ohistline;
	    return;
	}
	if (!hstrncmp(s, (char *)line, cs) && hstrcmp(s, (char *)line))
	    break;
    }

    sethistline((unsigned char *)s);	/* update command line.		*/
    cs = cpos;			/* reset cursor position.	*/
}
