/* ed/vi/ex style global commands, where first the file is scanned for
 *	matching lines, then for each such line, an action is performed.
 *	written for vile: Copyright (c) 1990-1999 by Paul Fox
 *
 * $Header: /usr/build/vile/vile/RCS/globals.c,v 1.46 2000/08/26 16:37:48 tom Exp $
 *
 */

#include	"estruct.h"
#include        "edef.h"
#include	"nefunc.h"

static	int	globber (CMD_ARGS, int g_or_v);

int
globals(int f, int n)
{
	return globber(f,n,'g');
}

int
vglobals(int f, int n)
{
	return globber(f,n,'v');
}

/* ARGSUSED */
static int
globber(int f GCC_UNUSED, int n GCC_UNUSED, int g_or_v)
{
	int c, s;
	register LINE *lp;
	register char *fnp;	/* ptr to the name of the cmd to exec */
	char	cmd[NLINE];
	const CMDFUNC *cfp;
	int foundone;
	int	status;
	WINDOW *wp, *sw_wp;
	L_NUM	before;
	int	save_report;

	c = kbd_delimiter();
	if ((status = readpattern("global pattern: ", searchpat,
				&gregexp, c, FALSE)) != TRUE) {
		mlforce("[No pattern.]");
		return status;
	}

	/* in some sense, it would be nice to search first, before
                making the user answer the next question, but the
                searching delay is too long, and unexpected in the
                middle of a command.  */

	fnp = kbd_engl("action to perform on each matching line: ", cmd);
	/* get the name of, and then the function to execute */
	if (!fnp) {
	        mlforce("[No function]");
		return FALSE;
	} else if ((cfp = engl2fnc(fnp)) == 0) {
	        return no_such_function(fnp);
	} else if ((cfp->c_flags & GLOBOK) == 0) {
	        mlforce("[Function not allowed]");
		return FALSE;
	}


	/* call the searcher, telling it to do line marking */
	s = fsearch(FALSE,0,TRUE,FALSE);
	if (s != TRUE)
		return s;

	calledbefore = FALSE;

	if (g_or_v == 'v') {  /* invert the sense of all the matches */
		for_each_line(lp, curbp)
			lflipmark(lp);
	}
	/* loop through the buffer -- we must clear the marks no matter what */
	s = TRUE;
	lp = lforw(buf_head(curbp));
	wp = sw_wp = curwp;
	/* loop until there are no marked lines in the buffer */
	foundone = FALSE;
	before = vl_line_count(curbp);
	save_report = global_g_val(GVAL_REPORT);
	for_ever {
		if (lp == win_head(wp)) {
			/* at the end -- only quit if we found no
				marks on the last pass through. otherwise,
				go through again */
			if (foundone)
				foundone = FALSE;
			else
				break;
			lsetnotmarked(lp); /* always unmark the header line */
		}
		if (lismarked(lp)) {
			foundone = TRUE;
			lsetnotmarked(lp);
			/* call the function, if there is one, and results
				have been ok so far */
			if (cfp && s) {
				if (!calledbefore && (cfp->c_flags & UNDO)) {
					if (b_val(wp->w_bufp,MDVIEW))
						return(rdonly());
					mayneedundo();
					set_global_g_val(GVAL_REPORT,0);
				}
				havemotion = &f_godotplus;
				wp->w_dot.l = lp;
				wp->w_dot.o = 0;
				s = call_cmdfunc(cfp, FALSE, 1);
				if (curwp != wp) {
				    /* function may have switched on us */
				    sw_wp = curwp;
				    (void)set_curwp(wp);
				}
				lp = wp->w_dot.l;
				havemotion = NULL;
				calledbefore = TRUE;
			}
		}
		lp = lforw(lp);
	}
	set_global_g_val(GVAL_REPORT,save_report);
	(void)line_report(before);
	/* if it tried to switch, do it now */
	(void)set_curwp(sw_wp);

	return s;
}
