/*
 * zle_word.c - word-related editor functions
 *
 * 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_word.pro"

/*
 * In principle we shouldn't consider a zero-length punctuation
 * character (i.e. a modifier of some sort) part of the word unless
 * the base character has.  However, we only consider them part of
 * a word if we so consider all alphanumerics, so the distinction
 * only applies if the characters are modifying something they probably
 * ought not to be modifying.  It's not really clear we need to
 * be clever about this not very useful case.
 */

/**/
int
forwardword(char **args)
{
    int n = zmult;

    if (n < 0) {
	int ret;
	zmult = -n;
	ret = backwardword(args);
	zmult = n;
	return ret;
    }
    while (n--) {
	while (zlecs != zlell && ZC_iword(zleline[zlecs]))
	    INCCS();
	if (wordflag && !n)
	    return 0;
	while (zlecs != zlell && !ZC_iword(zleline[zlecs]))
	    INCCS();
    }
    return 0;
}

#define Z_vialnum(X) (ZC_ialnum(X) || (ZWC('_') == X))

/**/
int
viforwardword(char **args)
{
    int n = zmult;

    if (n < 0) {
	int ret;
	zmult = -n;
	ret = backwardword(args);
	zmult = n;
	return ret;
    }
    while (n--) {
	int nl;
	if (Z_vialnum(zleline[zlecs]))
	    while (zlecs != zlell && Z_vialnum(zleline[zlecs]))
		INCCS();
	else
	    while (zlecs != zlell && !Z_vialnum(zleline[zlecs]) &&
		    !ZC_inblank(zleline[zlecs]))
		INCCS();
	if (wordflag && !n)
	    return 0;
	nl = (zleline[zlecs] == ZWC('\n'));
	while (zlecs != zlell && nl < 2 && ZC_inblank(zleline[zlecs])) {
	    INCCS();
	    nl += (zleline[zlecs] == ZWC('\n'));
	}
    }
    return 0;
}

/**/
int
viforwardblankword(char **args)
{
    int n = zmult;

    if (n < 0) {
	int ret;
	zmult = -n;
	ret = vibackwardblankword(args);
	zmult = n;
	return ret;
    }
    while (n--) {
	int nl;
	while (zlecs != zlell && !ZC_inblank(zleline[zlecs]))
	    INCCS();
	if (wordflag && !n)
	    return 0;
	nl = (zleline[zlecs] == ZWC('\n'));
	while (zlecs != zlell && nl < 2 && ZC_inblank(zleline[zlecs])) {
	    INCCS();
	    nl += (zleline[zlecs] == ZWC('\n'));
	}
    }
    return 0;
}

/**/
int
emacsforwardword(char **args)
{
    int n = zmult;

    if (n < 0) {
	int ret;
	zmult = -n;
	ret = emacsbackwardword(args);
	zmult = n;
	return ret;
    }
    while (n--) {
	while (zlecs != zlell && !ZC_iword(zleline[zlecs]))
	    INCCS();
	if (wordflag && !n)
	    return 0;
	while (zlecs != zlell && ZC_iword(zleline[zlecs]))
	    INCCS();
    }
    return 0;
}

/**/
int
viforwardblankwordend(UNUSED(char **args))
{
    int n = zmult;

    if (n < 0) {
	int ret;
	zmult = -n;
	ret = viforwardblankwordend(args);
	zmult = n;
	return ret;
    }
    while (n--) {
	while (zlecs != zlell) {
	    int pos = zlecs;
	    INCPOS(pos);
	    if (!ZC_inblank(zleline[pos]))
		break;
	    zlecs = pos;
	}
	while (zlecs != zlell) {
	    int pos = zlecs;
	    INCPOS(pos);
	    if (ZC_inblank(zleline[pos]))
		break;
	    zlecs = pos;
	}
    }
    if (zlecs != zlell && virangeflag)
	INCCS();
    return 0;
}

/**/
int
viforwardwordend(char **args)
{
    int n = zmult;

    if (n < 0) {
	int ret;
	zmult = -n;
	ret = vibackwardwordend(args);
	zmult = n;
	return ret;
    }
    while (n--) {
	int pos;
	while (zlecs != zlell) {
	    pos = zlecs;
	    INCPOS(pos);
	    if (!ZC_inblank(zleline[pos]))
		break;
	    zlecs = pos;
	}
	if (zlecs != zlell) {
	    pos = zlecs;
	    INCPOS(pos);
	    if (Z_vialnum(zleline[pos])) {
		for (;;) {
		    zlecs = pos;
		    if (zlecs == zlell)
			break;
		    INCPOS(pos);
		    if (!Z_vialnum(zleline[pos]))
			break;
		}
	    } else {
		for (;;) {
		    zlecs = pos;
		    if (zlecs == zlell)
			break;
		    INCPOS(pos);
		    if (Z_vialnum(zleline[pos]) || ZC_inblank(zleline[pos]))
			break;
		}
	    }
	}
    }
    if (zlecs != zlell && virangeflag)
	INCCS();
    return 0;
}

/**/
int
backwardword(char **args)
{
    int n = zmult;

    if (n < 0) {
	int ret;
	zmult = -n;
	ret = forwardword(args);
	zmult = n;
	return ret;
    }
    while (n--) {
	while (zlecs) {
	    int pos = zlecs;
	    DECPOS(pos);
	    if (ZC_iword(zleline[pos]))
		break;
	    zlecs = pos;
	}
	while (zlecs) {
	    int pos = zlecs;
	    DECPOS(pos);
	    if (!ZC_iword(zleline[pos]))
		break;
	    zlecs = pos;
	}
    }
    return 0;
}

/**/
int
vibackwardword(char **args)
{
    int n = zmult;

    if (n < 0) {
	int ret;
	zmult = -n;
	ret = backwardword(args);
	zmult = n;
	return ret;
    }
    while (n--) {
	int nl = 0;
	while (zlecs) {
	    DECCS();
	    if (!ZC_inblank(zleline[zlecs]))
		break;
	    nl += (zleline[zlecs] == ZWC('\n'));
	    if (nl == 2) {
		INCCS();
		break;
	    }
	}
	if (zlecs) {
	    int pos = zlecs;
	    if (Z_vialnum(zleline[pos])) {
		for (;;) {
		    zlecs = pos;
		    if (zlecs == 0)
			break;
		    DECPOS(pos);
		    if (!Z_vialnum(zleline[pos]))
			break;
		}
	    } else {
		for (;;) {
		    zlecs = pos;
		    if (zlecs == 0)
			break;
		    DECPOS(pos);
		    if (Z_vialnum(zleline[pos]) || ZC_inblank(zleline[pos]))
			break;
		}
	    }
	}
    }
    return 0;
}

/**/
int
vibackwardblankword(char **args)
{
    int n = zmult;

    if (n < 0) {
	int ret;
	zmult = -n;
	ret = viforwardblankword(args);
	zmult = n;
	return ret;
    }
    while (n--) {
	int nl = 0;
	while (zlecs) {
	    int pos = zlecs;
	    DECPOS(pos);
	    if (!ZC_inblank(zleline[pos]))
		break;
	    nl += (zleline[pos] == ZWC('\n'));
	    if (nl == 2) break;
	    zlecs = pos;
	}
	while (zlecs) {
	    int pos = zlecs;
	    DECPOS(pos);
	    if (ZC_inblank(zleline[pos]))
		break;
	    zlecs = pos;
	}
    }
    return 0;
}

/**/
int
vibackwardwordend(char **args)
{
    int n = zmult;

    if (n < 0) {
	int ret;
	zmult = -n;
	ret = viforwardwordend(args);
	zmult = n;
	return ret;
    }
    while (n-- && zlecs > 1) {
	int start = 0;
	if (Z_vialnum(zleline[zlecs]))
	    start = 1;
	else if (!ZC_inblank(zleline[zlecs]))
	    start = 2;
	DECCS();
	while (zlecs) {
	    int same = (start != 1) && ZC_iblank(zleline[zlecs]);
	    if (start)
		same |= Z_vialnum(zleline[zlecs]);
	    if (same == (start == 2))
		break;
	    DECCS();
	}
	while (zlecs && ZC_iblank(zleline[zlecs]))
	    DECCS();
    }
    return 0;
}

/**/
int
vibackwardblankwordend(char **args)
{
    int n = zmult;

    if (n < 0) {
	int ret;
	zmult = -n;
	ret = viforwardblankwordend(args);
	zmult = n;
	return ret;
    }
    while (n--) {
	while (zlecs && !ZC_inblank(zleline[zlecs]))
	    DECCS();
	while (zlecs && ZC_inblank(zleline[zlecs]))
	    DECCS();
    }
    return 0;
}

/**/
int
emacsbackwardword(char **args)
{
    int n = zmult;

    if (n < 0) {
	int ret;
	zmult = -n;
	ret = emacsforwardword(args);
	zmult = n;
	return ret;
    }
    while (n--) {
	while (zlecs) {
	    int pos = zlecs;
	    DECPOS(pos);
	    if (ZC_iword(zleline[pos]))
		break;
	    zlecs = pos;
	}
	while (zlecs) {
	    int pos = zlecs;
	    DECPOS(pos);
	    if (!ZC_iword(zleline[pos]))
		break;
	    zlecs = pos;
	}
    }
    return 0;
}

/**/
int
backwarddeleteword(char **args)
{
    int x = zlecs, n = zmult;

    if (n < 0) {
	int ret;
	zmult = -n;
	ret = deleteword(args);
	zmult = n;
	return ret;
    }
    while (n--) {
	while (x) {
	    int pos = x;
	    DECPOS(pos);
	    if (ZC_iword(zleline[pos]))
		break;
	    x = pos;
	}
	while (x) {
	    int pos = x;
	    DECPOS(pos);
	    if (!ZC_iword(zleline[pos]))
		break;
	    x = pos;
	}
    }
    backdel(zlecs - x, CUT_RAW);
    return 0;
}

/**/
int
vibackwardkillword(UNUSED(char **args))
{
    int x = zlecs, lim = (viinsbegin > findbol()) ? viinsbegin : findbol();
    int n = zmult;

    if (n < 0)
	return 1;
/* this taken from "vibackwardword" */
    while (n--) {
	while (x > lim) {
	    int pos = x;
	    DECPOS(pos);
	    if (!ZC_iblank(zleline[pos]))
		break;
	    x = pos;
	}
	if (x > lim) {
	    int pos = x;
	    DECPOS(pos);
	    if (Z_vialnum(zleline[pos])) {
		for (;;) {
		    x = pos;
		    if (x <= lim)
			break;
		    DECPOS(pos);
		    if (!Z_vialnum(zleline[pos]))
			break;
		}
	    } else {
		for (;;) {
		    x = pos;
		    if (x <= lim)
			break;
		    DECPOS(pos);
		    if (Z_vialnum(zleline[pos]) || ZC_iblank(zleline[pos]))
			break;
		}
	    }
	}
    }
    backkill(zlecs - x, CUT_FRONT|CUT_RAW);
    return 0;
}

/**/
int
backwardkillword(char **args)
{
    int x = zlecs;
    int n = zmult;

    if (n < 0) {
	int ret;
	zmult = -n;
	ret = killword(args);
	zmult = n;
	return ret;
    }
    while (n--) {
	while (x) {
	    int pos = x;
	    DECPOS(pos);
	    if (ZC_iword(zleline[pos]))
		break;
	    x = pos;
	}
	while (x) {
	    int pos = x;
	    DECPOS(pos);
	    if (!ZC_iword(zleline[pos]))
		break;
	    x = pos;
	}
    }
    backkill(zlecs - x, CUT_FRONT|CUT_RAW);
    return 0;
}

/**/
int
upcaseword(UNUSED(char **args))
{
    int n = zmult;
    int neg = n < 0, ocs = zlecs;

    if (neg)
	n = -n;
    while (n--) {
	while (zlecs != zlell && !ZC_iword(zleline[zlecs]))
	    INCCS();
	while (zlecs != zlell && ZC_iword(zleline[zlecs])) {
	    zleline[zlecs] = ZC_toupper(zleline[zlecs]);
	    INCCS();
	}
    }
    if (neg)
	zlecs = ocs;
    return 0;
}

/**/
int
downcaseword(UNUSED(char **args))
{
    int n = zmult;
    int neg = n < 0, ocs = zlecs;

    if (neg)
	n = -n;
    while (n--) {
	while (zlecs != zlell && !ZC_iword(zleline[zlecs]))
	    INCCS();
	while (zlecs != zlell && ZC_iword(zleline[zlecs])) {
	    zleline[zlecs] = ZC_tolower(zleline[zlecs]);
	    INCCS();
	}
    }
    if (neg)
	zlecs = ocs;
    return 0;
}

/**/
int
capitalizeword(UNUSED(char **args))
{
    int first, n = zmult;
    int neg = n < 0, ocs = zlecs;

    if (neg)
	n = -n;
    while (n--) {
	first = 1;
	while (zlecs != zlell && !ZC_iword(zleline[zlecs]))
	    INCCS();
	while (zlecs != zlell && ZC_iword(zleline[zlecs]) && !ZC_ialpha(zleline[zlecs]))
	    INCCS();
	while (zlecs != zlell && ZC_iword(zleline[zlecs])) {
	    zleline[zlecs] = (first) ? ZC_toupper(zleline[zlecs]) :
		ZC_tolower(zleline[zlecs]);
	    first = 0;
	    INCCS();
	}
    }
    if (neg)
	zlecs = ocs;
    return 0;
}

/**/
int
deleteword(char **args)
{
    int x = zlecs;
    int n = zmult;

    if (n < 0) {
	int ret;
	zmult = -n;
	ret = backwarddeleteword(args);
	zmult = n;
	return ret;
    }
    while (n--) {
	while (x != zlell && !ZC_iword(zleline[x]))
	    INCPOS(x);
	while (x != zlell && ZC_iword(zleline[x]))
	    INCPOS(x);
    }
    foredel(x - zlecs, CUT_RAW);
    return 0;
}

/**/
int
killword(char **args)
{
    int x = zlecs;
    int n = zmult;

    if (n < 0) {
	int ret;
	zmult = -n;
	ret = backwardkillword(args);
	zmult = n;
	return ret;
    }
    while (n--) {
	while (x != zlell && !ZC_iword(zleline[x]))
	    INCPOS(x);
	while (x != zlell && ZC_iword(zleline[x]))
	    INCPOS(x);
    }
    forekill(x - zlecs, CUT_RAW);
    return 0;
}

/**/
int
transposewords(UNUSED(char **args))
{
    int p1, p2, p3, p4, len, x = zlecs, pos;
    ZLE_STRING_T temp, pp;
    int n = zmult;
    int neg = n < 0, ocs = zlecs;

    if (neg)
	n = -n;
    while (n--) {
	while (x != zlell && zleline[x] != ZWC('\n') && !ZC_iword(zleline[x]))
	    INCPOS(x);
	if (x == zlell || zleline[x] == ZWC('\n')) {
	    x = zlecs;
	    while (x) {
		if (ZC_iword(zleline[x]))
		    break;
		pos = x;
		DECPOS(pos);
		if (zleline[pos] == ZWC('\n'))
		    break;
		x = pos;
	    }
	    if (!x)
		return 1;
	    pos = x;
	    DECPOS(pos);
	    if (zleline[pos] == ZWC('\n'))
		return 1;
	}
	for (p4 = x; p4 != zlell && ZC_iword(zleline[p4]); INCPOS(p4))
	    ;
	for (p3 = p4; p3; ) {
	    pos = p3;
	    DECPOS(pos);
	    if (!ZC_iword(zleline[pos]))
		break;
	    p3 = pos;
	}
	if (!p3)
	    return 1;
	for (p2 = p3; p2; ) {
	    pos = p2;
	    DECPOS(pos);
	    if (ZC_iword(zleline[pos]))
		break;
	    p2 = pos;
	}
	if (!p2)
	    return 1;
	for (p1 = p2; p1; ) {
	    pos = p1;
	    DECPOS(pos);
	    if (!ZC_iword(zleline[pos]))
		break;
	    p1 = pos;
	}
	pp = temp = (ZLE_STRING_T)zhalloc((p4 - p1)*ZLE_CHAR_SIZE);
	len = p4 - p3;
	ZS_memcpy(pp, zleline + p3, len);
	pp += len;
	len = p3 - p2;
	ZS_memcpy(pp, zleline + p2, len);
	pp += len;
	ZS_memcpy(pp, zleline + p1, p2 - p1);

	ZS_memcpy(zleline + p1, temp, p4 - p1);

	zlecs = p4;
    }
    if (neg)
	zlecs = ocs;
    return 0;
}
