/*
 *
 * zle_hist.c - history editing
 *
 * This file is part of zsh, the Z shell.
 *
 * This software is Copyright 1992 by Paul Falstad
 *
 * Permission is hereby granted to copy, reproduce, redistribute or otherwise
 * use this software as long as: there is no monetary profit gained
 * specifically from the use or reproduction of this software, it is not
 * sold, rented, traded or otherwise marketed, and this copyright notice is
 * included prominently in any copy made. 
 *
 * The author make no claims as to the fitness or correctness of this software
 * for any use whatsoever, and it is provided as is. Any use of this software
 * is at the user's own risk. 
 *
 */

#define ZLE
#include "zsh.h"

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

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

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

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

void uplineorhistory() /**/
{
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;
	}
}

void uplineorsearch() /**/
{
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;
	}
}

void downlineorhistory() /**/
{
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;
	}
}

void downlineorsearch() /**/
{
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;
	}
}

void acceptlineanddownhistory() /**/
{
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() /**/
{
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() /**/
{
int t0,ohistline = histline;
char *s;

	if (histline == curhist)
		{
		if (curhistline)
			free(curhistline);
		curhistline = ztrdup(UTOSCP(line));
		}
	if (lastcmd & ZLE_HISTSEARCH) t0 = histpos;
	else for (t0 = 0; line[t0] && iword(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() /**/
{
int t0,ohistline = histline;
char *s;

	if (histline == curhist)
		{
		if (curhistline)
			free(curhistline);
		curhistline = ztrdup(UTOSCP(line));
		}
	if (lastcmd & ZLE_HISTSEARCH) t0 = histpos;
	else for (t0 = 0; line[t0] && iword(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() /**/
{
	if (findbol())
		cs = 0;
	else
		beginningofhistory();
}

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

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

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

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

void insertlastword() /**/
{
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 len = strlen(lastinsert);
	    int pos = cs;
	    if (	lastpos <= pos &&
			len == pos - lastpos &&
			strncmp(lastinsert, (char *) &line[lastpos], len) == 0) {
		evhist = --lasthist;
		cs = lastpos;
		foredel(pos-cs);
	    }
	    free(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(ev) /**/
int ev;
{
	if (ev > curhist)
		return NULL;
	return ((ev == curhist) ? curhistline : quietgetevent(ev));
}

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

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

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

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

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

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

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

	strcpy(ibuf,"i-search: ");
	statusline = ibuf;
	oldl = ztrdup(UTOSCP(line));
	if (histline == curhist)
		{
		if (curhistline)
			free(curhistline);
		curhistline = ztrdup(UTOSCP(line));
		}
	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 ((ch = getkey(1)) == -1)
			break;
		if (ch == 22 || ch == 17) {
			if ((ch = getkey(1)) == -1)
				break;
		} else if (ch == 24) { /* ^XS and ^XR */
			if ((ch = getkey(1)) == -1)
				break;
			if (ch != 's' && ch != 'r') {
				ungetkey(24);
				ungetkey(ch);
				break;
			}
			ungetkey(ch & 0x1f);
			continue;
		} else if (ch == 8 || ch == 127) {
			if (sbptr)
				sbuf[--sbptr] = '\0';
			else
				feep();
			histline = ohl;
			continue;
		} else if (ch == 7 || ch == 3) {
			setline(oldl);
			cs = ocs;
			histline = ohl;
			statusline = NULL;
			break;
		} else if (ch == 27)
			break;
		else if (ch == 10 || ch == 13) {
			ungetkey(ch);
			break;
		} else if (ch == 18) {
			ohl = (histline += (dir = -1));
			chequiv = 1;
			continue;
		} else if (ch == 19) {
			ohl = (histline += (dir = 1));
			chequiv = 1;
			continue;
		} else if (!(ch & 0x60)) {
			ungetkey(ch);
			break;
		}
		if (!nomatch && sbptr != 39 && !icntrl(ch)) {
			sbuf[sbptr++] = ch;
			sbuf[sbptr] = '\0';
		}
	}
	free(oldl);
	statusline = NULL;
}

void acceptandinfernexthistory() /**/
{
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() /**/
{
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() /**/
{
char *s;

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

int getvisrchstr() /**/
{
static char sbuf[80];
int sptr = 1;

	if (visrchstr)
		{
		free(visrchstr);
		visrchstr = NULL;
		}
	statusline = sbuf;
	sbuf[0] = c;
	sbuf[1] = '\0';
	while (sptr)
		{
		refresh();
		c = getkey(0);
		if (c == '\r' || c == '\n' || c == '\033')
			{
			visrchstr = ztrdup(sbuf+1);
			return 1;
			}
		if (c == '\b' || c == 127)
			{
			sbuf[--sptr] = '\0';
			continue;
			}
		if (sptr != 79)
			{
			sbuf[sptr++] = c;
			sbuf[sptr] = '\0';
			}
		}
	return 0;
}

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

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

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

	if (!visrchstr)
		{
		feep();
		return;
		}
	t0 = strlen(visrchstr);
	if (histline == curhist)
		{
		if (curhistline)
			free(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() /**/
{
	visrchsense = -visrchsense;
	virepeatsearch();
	visrchsense = -visrchsense;
}

