119304Speter/*-
219304Speter * Copyright (c) 1992, 1993, 1994
319304Speter *	The Regents of the University of California.  All rights reserved.
419304Speter * Copyright (c) 1992, 1993, 1994, 1995, 1996
519304Speter *	Keith Bostic.  All rights reserved.
619304Speter *
719304Speter * See the LICENSE file for redistribution information.
819304Speter */
919304Speter
1019304Speter#include "config.h"
1119304Speter
1219304Speter#ifndef lint
1319304Speterstatic const char sccsid[] = "@(#)ex_at.c	10.12 (Berkeley) 9/15/96";
1419304Speter#endif /* not lint */
1519304Speter
1619304Speter#include <sys/types.h>
1719304Speter#include <sys/queue.h>
1819304Speter
1919304Speter#include <bitstring.h>
2019304Speter#include <ctype.h>
2119304Speter#include <limits.h>
2219304Speter#include <stdio.h>
2319304Speter#include <stdlib.h>
2419304Speter#include <string.h>
2519304Speter
2619304Speter#include "../common/common.h"
2719304Speter
2819304Speter/*
2919304Speter * ex_at -- :@[@ | buffer]
3019304Speter *	    :*[* | buffer]
3119304Speter *
3219304Speter *	Execute the contents of the buffer.
3319304Speter *
3419304Speter * PUBLIC: int ex_at __P((SCR *, EXCMD *));
3519304Speter */
3619304Speterint
3719304Speterex_at(sp, cmdp)
3819304Speter	SCR *sp;
3919304Speter	EXCMD *cmdp;
4019304Speter{
4119304Speter	CB *cbp;
4219304Speter	CHAR_T name;
4319304Speter	EXCMD *ecp;
4419304Speter	RANGE *rp;
4519304Speter	TEXT *tp;
4619304Speter	size_t len;
4719304Speter	char *p;
4819304Speter
4919304Speter	/*
5019304Speter	 * !!!
5119304Speter	 * Historically, [@*]<carriage-return> and [@*][@*] executed the most
5219304Speter	 * recently executed buffer in ex mode.
5319304Speter	 */
5419304Speter	name = FL_ISSET(cmdp->iflags, E_C_BUFFER) ? cmdp->buffer : '@';
5519304Speter	if (name == '@' || name == '*') {
5619304Speter		if (!F_ISSET(sp, SC_AT_SET)) {
5719304Speter			ex_emsg(sp, NULL, EXM_NOPREVBUF);
5819304Speter			return (1);
5919304Speter		}
6019304Speter		name = sp->at_lbuf;
6119304Speter	}
6219304Speter	sp->at_lbuf = name;
6319304Speter	F_SET(sp, SC_AT_SET);
6419304Speter
6519304Speter	CBNAME(sp, cbp, name);
6619304Speter	if (cbp == NULL) {
6719304Speter		ex_emsg(sp, KEY_NAME(sp, name), EXM_EMPTYBUF);
6819304Speter		return (1);
6919304Speter	}
7019304Speter
7119304Speter	/*
7219304Speter	 * !!!
7319304Speter	 * Historically the @ command took a range of lines, and the @ buffer
7419304Speter	 * was executed once per line.  The historic vi could be trashed by
7519304Speter	 * this because it didn't notice if the underlying file changed, or,
7619304Speter	 * for that matter, if there were no more lines on which to operate.
7719304Speter	 * For example, take a 10 line file, load "%delete" into a buffer,
7819304Speter	 * and enter :8,10@<buffer>.
7919304Speter	 *
8019304Speter	 * The solution is a bit tricky.  If the user specifies a range, take
8119304Speter	 * the same approach as for global commands, and discard the command
8219304Speter	 * if exit or switch to a new file/screen.  If the user doesn't specify
8319304Speter	 * the  range, continue to execute after a file/screen switch, which
8419304Speter	 * means @ buffers are still useful in a multi-screen environment.
8519304Speter	 */
8619304Speter	CALLOC_RET(sp, ecp, EXCMD *, 1, sizeof(EXCMD));
8719304Speter	CIRCLEQ_INIT(&ecp->rq);
8819304Speter	CALLOC_RET(sp, rp, RANGE *, 1, sizeof(RANGE));
8919304Speter	rp->start = cmdp->addr1.lno;
9019304Speter	if (F_ISSET(cmdp, E_ADDR_DEF)) {
9119304Speter		rp->stop = rp->start;
9219304Speter		FL_SET(ecp->agv_flags, AGV_AT_NORANGE);
9319304Speter	} else {
9419304Speter		rp->stop = cmdp->addr2.lno;
9519304Speter		FL_SET(ecp->agv_flags, AGV_AT);
9619304Speter	}
9719304Speter	CIRCLEQ_INSERT_HEAD(&ecp->rq, rp, q);
9819304Speter
9919304Speter	/*
10019304Speter	 * Buffers executed in ex mode or from the colon command line in vi
10119304Speter	 * were ex commands.  We can't push it on the terminal queue, since
10219304Speter	 * it has to be executed immediately, and we may be in the middle of
10319304Speter	 * an ex command already.  Push the command on the ex command stack.
10419304Speter	 * Build two copies of the command.  We need two copies because the
10519304Speter	 * ex parser may step on the command string when it's parsing it.
10619304Speter	 */
10719304Speter	for (len = 0, tp = cbp->textq.cqh_last;
10819304Speter	    tp != (void *)&cbp->textq; tp = tp->q.cqe_prev)
10919304Speter		len += tp->len + 1;
11019304Speter
11119304Speter	MALLOC_RET(sp, ecp->cp, char *, len * 2);
11219304Speter	ecp->o_cp = ecp->cp;
11319304Speter	ecp->o_clen = len;
11419304Speter	ecp->cp[len] = '\0';
11519304Speter
11619304Speter	/* Copy the buffer into the command space. */
11719304Speter	for (p = ecp->cp + len, tp = cbp->textq.cqh_last;
11819304Speter	    tp != (void *)&cbp->textq; tp = tp->q.cqe_prev) {
11919304Speter		memcpy(p, tp->lb, tp->len);
12019304Speter		p += tp->len;
12119304Speter		*p++ = '\n';
12219304Speter	}
12319304Speter
12419304Speter	LIST_INSERT_HEAD(&sp->gp->ecq, ecp, q);
12519304Speter	return (0);
12619304Speter}
127