/*
 * $Id: zle_utils.c,v 1.9 1995/05/30 06:21:41 coleman Exp coleman $
 *
 * zle_utils.c - miscellaneous line editor utilities
 *
 * 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"

/* 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) + 1);
}

/* insert space for ct chars at cursor position */

/**/
void
spaceinline(int ct)
{
    int i;

    while (ct + ll > linesz)
	line = (unsigned char *)realloc(line, (linesz *= 4) + 1);
    for (i = ll; i >= cs; i--)
	line[i + ct] = line[i];
    ll += ct;
    line[ll] = '\0';
}

/**/
void
backkill(int ct, int dir)
{
    int i = (cs -= ct);

    cut(i, ct, dir);
    while ((line[i] = line[i + ct]))
	i++;
    ll -= ct;
}

/**/
void
forekill(int ct, int dir)
{
    int i = cs;

    cut(i, ct, dir);
    while ((line[i] = line[i + ct]))
	i++;
    ll -= ct;
}

extern owrite;

/**/
void
cut(int i, int ct, int dir)
{
    if (vibufspec) {
	if (vilinebuf[vibufspec] && !vilinerange)
	    owrite = 1;
	if (owrite || !vibuf[vibufspec]) {
	    if (vibuf[vibufspec])
		free(vibuf[vibufspec]);
	    vibuf[vibufspec] = (char *)zalloc(ct + 1);
	    ztrncpy(vibuf[vibufspec], UTOSCP(line + i), ct);
	} else {
	    int len = strlen(vibuf[vibufspec]);

	    if (vilinerange)
		vibuf[vibufspec][len++] = '\n';
	    vibuf[vibufspec] = realloc(vibuf[vibufspec], ct + len);
	    ztrncpy(vibuf[vibufspec] + len, UTOSCP(line + i), ct);
	}
	vilinebuf[vibufspec] = vilinerange;
	vibufspec = 0;
	return;
    }
    if (!cutbuf)
	cutbuf = ztrdup("");
    else if (!(lastcmd & ZLE_KILL)) {
	kringnum = (kringnum + 1) & (KRINGCT - 1);
	if (kring[kringnum])
	    free(kring[kringnum]);
	kring[kringnum] = cutbuf;
	cutbuf = ztrdup("");
    }
    if (dir) {
	char *s = (char *)zalloc(strlen(cutbuf) + ct + 1);

	strncpy(s, (char *)line + i, ct);
	strcpy(s + ct, cutbuf);
	free(cutbuf);
	cutbuf = s;
    } else {
	int x;

	cutbuf = realloc(cutbuf, (x = strlen(cutbuf)) + ct + 1);
	ztrncpy(cutbuf + x, UTOSCP(line + i), ct);
    }
    linecutbuf = vilinerange;
}

/**/
void
backdel(int ct)
{
    int i = (cs -= ct);

    while ((line[i] = line[i + ct]))
	i++;
    ll -= ct;
}

/**/
void
foredel(int ct)
{
    int i = cs;

    while ((line[i] = line[i + ct]))
	i++;
    ll -= ct;
}

/**/
void
setline(char *s)
{
    sizeline(strlen(s));
    strcpy((char *)line, s);
    cs = ll = strlen(s);
    if (cs && bindtab == altbindtab)
	cs--;
}

/**/
void
sethistline(unsigned char *s)
{
    setline(UTOSCP(s));
    for (s = line; *s; s++)
	if (*s == STOUC(HISTSPACE))
	    *s = ' ';
}

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

static int lastlinelen;

/**/
void
initundo(void)
{
    int t0;

    for (t0 = 0; t0 != UNDOCT; t0++)
	undos[t0].change = NULL;
    undoct = 0;
    lastline = (unsigned char *)zalloc(lastlinelen = (ll + 1 < 32) ? 32 : ll + 1);
    strcpy((char *)lastline, (char *)line);
    lastcs = cs;
}

/**/
void
addundo(void)
{
    int pf, sf;
    unsigned char *s, *s2, *t, *t2;
    struct undoent *ue;

    for (s = line, t = lastline; *s && *s == *t; s++, t++);
    if (!*s && !*t)
	return;
    pf = s - line;
    for (s2 = (unsigned char *)line + strlen((char *)line),
	 t2 = lastline + strlen((char *)lastline);
	 s2 > s && t > t2 && s2[-1] == t2[-1]; s2--, t2--);
    sf = strlen((char *)s2);
    ue = undos + (undoct = (UNDOCT - 1) & (undoct + 1));
    ue->pref = pf;
    ue->suff = sf;
    ue->len = t2 - t;
    ue->cs = lastcs;
    strncpy(ue->change = (char *)halloc(ue->len), (char *)t, ue->len);
    while (ll + 1 > lastlinelen) {
	free(lastline);
	lastline = (unsigned char *)zalloc(lastlinelen *= 2);
    }
    strcpy((char *)lastline, (char *)line);
    lastcs = cs;
}

/**/
int
hstrncmp(char *s, char *t, int len)
{
    while (len && *s && (*s == *t || (*s == ' ' && *t == HISTSPACE) ||
			 (*s == HISTSPACE && *t == ' ')))
	s++, t++, len--;
    return len;
}

/**/
int
hstrcmp(char *s, char *t)
{
    while (*s && (*s == *t || (*s == ' ' && *t == HISTSPACE) ||
		  (*s == HISTSPACE && *t == ' ')))
	s++, t++;
    return !(*s == '\0' && *t == '\0');
}

/**/
char *
hstrnstr(char *s, char *t, int len)
{
    for (; *s; s++)
	if (!hstrncmp(t, s, len))
	    return s;
    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)
{
    char 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 = 'n';
    else
	c = tulower(c);

    /* echo response and return */
    putc(c, shout);
    return (int)c;
}
