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
13254225Speterstatic const char sccsid[] = "$Id: ex_append.c,v 10.34 2001/06/25 15:19:14 skimo Exp $";
1419304Speter#endif /* not lint */
1519304Speter
1619304Speter#include <sys/types.h>
1719304Speter#include <sys/queue.h>
18254225Speter#include <sys/time.h>
1919304Speter
2019304Speter#include <bitstring.h>
2119304Speter#include <limits.h>
2219304Speter#include <stdio.h>
2319304Speter#include <string.h>
2419304Speter#include <unistd.h>
2519304Speter
2619304Speter#include "../common/common.h"
2719304Speter
2819304Speterenum which {APPEND, CHANGE, INSERT};
2919304Speter
3019304Speterstatic int ex_aci __P((SCR *, EXCMD *, enum which));
3119304Speter
3219304Speter/*
3319304Speter * ex_append -- :[line] a[ppend][!]
3419304Speter *	Append one or more lines of new text after the specified line,
3519304Speter *	or the current line if no address is specified.
3619304Speter *
3719304Speter * PUBLIC: int ex_append __P((SCR *, EXCMD *));
3819304Speter */
3919304Speterint
40254225Speterex_append(SCR *sp, EXCMD *cmdp)
4119304Speter{
4219304Speter	return (ex_aci(sp, cmdp, APPEND));
4319304Speter}
4419304Speter
4519304Speter/*
4619304Speter * ex_change -- :[line[,line]] c[hange][!] [count]
4719304Speter *	Change one or more lines to the input text.
4819304Speter *
4919304Speter * PUBLIC: int ex_change __P((SCR *, EXCMD *));
5019304Speter */
5119304Speterint
52254225Speterex_change(SCR *sp, EXCMD *cmdp)
5319304Speter{
5419304Speter	return (ex_aci(sp, cmdp, CHANGE));
5519304Speter}
5619304Speter
5719304Speter/*
5819304Speter * ex_insert -- :[line] i[nsert][!]
5919304Speter *	Insert one or more lines of new text before the specified line,
6019304Speter *	or the current line if no address is specified.
6119304Speter *
6219304Speter * PUBLIC: int ex_insert __P((SCR *, EXCMD *));
6319304Speter */
6419304Speterint
65254225Speterex_insert(SCR *sp, EXCMD *cmdp)
6619304Speter{
6719304Speter	return (ex_aci(sp, cmdp, INSERT));
6819304Speter}
6919304Speter
7019304Speter/*
7119304Speter * ex_aci --
7219304Speter *	Append, change, insert in ex.
7319304Speter */
7419304Speterstatic int
75254225Speterex_aci(SCR *sp, EXCMD *cmdp, enum which cmd)
7619304Speter{
7719304Speter	CHAR_T *p, *t;
7819304Speter	GS *gp;
7919304Speter	TEXT *tp;
80254225Speter	TEXTH tiq[] = {{ 0 }};
81254225Speter	recno_t cnt = 0, lno;
8219304Speter	size_t len;
8319304Speter	u_int32_t flags;
8419304Speter	int need_newline;
8519304Speter
8619304Speter	gp = sp->gp;
8719304Speter	NEEDFILE(sp, cmdp);
8819304Speter
8919304Speter	/*
9019304Speter	 * If doing a change, replace lines for as long as possible.  Then,
9119304Speter	 * append more lines or delete remaining lines.  Changes to an empty
9219304Speter	 * file are appends, inserts are the same as appends to the previous
9319304Speter	 * line.
9419304Speter	 *
9519304Speter	 * !!!
9619304Speter	 * Set the address to which we'll append.  We set sp->lno to this
9719304Speter	 * address as well so that autoindent works correctly when get text
9819304Speter	 * from the user.
9919304Speter	 */
10019304Speter	lno = cmdp->addr1.lno;
10119304Speter	sp->lno = lno;
10219304Speter	if ((cmd == CHANGE || cmd == INSERT) && lno != 0)
10319304Speter		--lno;
10419304Speter
10519304Speter	/*
10619304Speter	 * !!!
10719304Speter	 * If the file isn't empty, cut changes into the unnamed buffer.
10819304Speter	 */
10919304Speter	if (cmd == CHANGE && cmdp->addr1.lno != 0 &&
11019304Speter	    (cut(sp, NULL, &cmdp->addr1, &cmdp->addr2, CUT_LINEMODE) ||
11119304Speter	    del(sp, &cmdp->addr1, &cmdp->addr2, 1)))
11219304Speter		return (1);
11319304Speter
11419304Speter	/*
11519304Speter	 * !!!
11619304Speter	 * Anything that was left after the command separator becomes part
11719304Speter	 * of the inserted text.  Apparently, it was common usage to enter:
11819304Speter	 *
11919304Speter	 *	:g/pattern/append|stuff1
12019304Speter	 *
12119304Speter	 * and append the line of text "stuff1" to the lines containing the
12219304Speter	 * pattern.  It was also historically legal to enter:
12319304Speter	 *
12419304Speter	 *	:append|stuff1
12519304Speter	 *	stuff2
12619304Speter	 *	.
12719304Speter	 *
12819304Speter	 * and the text on the ex command line would be appended as well as
12919304Speter	 * the text inserted after it.  There was an historic bug however,
13019304Speter	 * that the user had to enter *two* terminating lines (the '.' lines)
13119304Speter	 * to terminate text input mode, in this case.  This whole thing
13219304Speter	 * could be taken too far, however.  Entering:
13319304Speter	 *
13419304Speter	 *	:append|stuff1\
13519304Speter	 *	stuff2
13619304Speter	 *	stuff3
13719304Speter	 *	.
13819304Speter	 *
13919304Speter	 * i.e. mixing and matching the forms confused the historic vi, and,
14019304Speter	 * not only did it take two terminating lines to terminate text input
14119304Speter	 * mode, but the trailing backslashes were retained on the input.  We
14219304Speter	 * match historic practice except that we discard the backslashes.
14319304Speter	 *
14419304Speter	 * Input lines specified on the ex command line lines are separated by
14519304Speter	 * <newline>s.  If there is a trailing delimiter an empty line was
14619304Speter	 * inserted.  There may also be a leading delimiter, which is ignored
14719304Speter	 * unless it's also a trailing delimiter.  It is possible to encounter
14819304Speter	 * a termination line, i.e. a single '.', in a global command, but not
14919304Speter	 * necessary if the text insert command was the last of the global
15019304Speter	 * commands.
15119304Speter	 */
15219304Speter	if (cmdp->save_cmdlen != 0) {
15319304Speter		for (p = cmdp->save_cmd,
15419304Speter		    len = cmdp->save_cmdlen; len > 0; p = t) {
15519304Speter			for (t = p; len > 0 && t[0] != '\n'; ++t, --len);
15619304Speter			if (t != p || len == 0) {
15719304Speter				if (F_ISSET(sp, SC_EX_GLOBAL) &&
15819304Speter				    t - p == 1 && p[0] == '.') {
15919304Speter					++t;
16019304Speter					if (len > 0)
16119304Speter						--len;
16219304Speter					break;
16319304Speter				}
16419304Speter				if (db_append(sp, 1, lno++, p, t - p))
16519304Speter					return (1);
16619304Speter			}
16719304Speter			if (len != 0) {
16819304Speter				++t;
16919304Speter				if (--len == 0 &&
170254225Speter				    db_append(sp, 1, lno++, NULL, 0))
17119304Speter					return (1);
17219304Speter			}
17319304Speter		}
17419304Speter		/*
17519304Speter		 * If there's any remaining text, we're in a global, and
17619304Speter		 * there's more command to parse.
17719304Speter		 *
17819304Speter		 * !!!
17919304Speter		 * We depend on the fact that non-global commands will eat the
18019304Speter		 * rest of the command line as text input, and before getting
18119304Speter		 * any text input from the user.  Otherwise, we'd have to save
18219304Speter		 * off the command text before or during the call to the text
18319304Speter		 * input function below.
18419304Speter		 */
18519304Speter		if (len != 0)
18619304Speter			cmdp->save_cmd = t;
18719304Speter		cmdp->save_cmdlen = len;
18819304Speter	}
18919304Speter
19019304Speter	if (F_ISSET(sp, SC_EX_GLOBAL)) {
19119304Speter		if ((sp->lno = lno) == 0 && db_exist(sp, 1))
19219304Speter			sp->lno = 1;
19319304Speter		return (0);
19419304Speter	}
19519304Speter
19619304Speter	/*
19719304Speter	 * If not in a global command, read from the terminal.
19819304Speter	 *
19919304Speter	 * If this code is called by vi, we want to reset the terminal and use
20019304Speter	 * ex's line get routine.  It actually works fine if we use vi's get
20119304Speter	 * routine, but it doesn't look as nice.  Maybe if we had a separate
20219304Speter	 * window or something, but getting a line at a time looks awkward.
20319304Speter	 * However, depending on the screen that we're using, that may not
20419304Speter	 * be possible.
20519304Speter	 */
20619304Speter	if (F_ISSET(sp, SC_VI)) {
20719304Speter		if (gp->scr_screen(sp, SC_EX)) {
208254225Speter			ex_wemsg(sp, cmdp->cmd->name, EXM_NOCANON);
20919304Speter			return (1);
21019304Speter		}
21119304Speter
21219304Speter		/* If we're still in the vi screen, move out explicitly. */
21319304Speter		need_newline = !F_ISSET(sp, SC_SCR_EXWROTE);
21419304Speter		F_SET(sp, SC_SCR_EX | SC_SCR_EXWROTE);
21519304Speter		if (need_newline)
21619304Speter			(void)ex_puts(sp, "\n");
21719304Speter
21819304Speter		/*
21919304Speter		 * !!!
22019304Speter		 * Users of historical versions of vi sometimes get confused
22119304Speter		 * when they enter append mode, and can't seem to get out of
22219304Speter		 * it.  Give them an informational message.
22319304Speter		 */
22419304Speter		(void)ex_puts(sp,
22519304Speter		    msg_cat(sp, "273|Entering ex input mode.", NULL));
22619304Speter		(void)ex_puts(sp, "\n");
22719304Speter		(void)ex_fflush(sp);
22819304Speter	}
22919304Speter
23019304Speter	/*
23119304Speter	 * Set input flags; the ! flag turns off autoindent for append,
23219304Speter	 * change and insert.
23319304Speter	 */
23419304Speter	LF_INIT(TXT_DOTTERM | TXT_NUMBER);
23519304Speter	if (!FL_ISSET(cmdp->iflags, E_C_FORCE) && O_ISSET(sp, O_AUTOINDENT))
23619304Speter		LF_SET(TXT_AUTOINDENT);
23719304Speter	if (O_ISSET(sp, O_BEAUTIFY))
23819304Speter		LF_SET(TXT_BEAUTIFY);
23919304Speter
24019304Speter	/*
24119304Speter	 * This code can't use the common screen TEXTH structure (sp->tiq),
24219304Speter	 * as it may already be in use, e.g. ":append|s/abc/ABC/" would fail
24319304Speter	 * as we are only halfway through the text when the append code fires.
24419304Speter	 * Use a local structure instead.  (The ex code would have to use a
24519304Speter	 * local structure except that we're guaranteed to finish remaining
24619304Speter	 * characters in the common TEXTH structure when they were inserted
24719304Speter	 * into the file, above.)
24819304Speter	 */
249254225Speter	TAILQ_INIT(tiq);
25019304Speter
251254225Speter	if (ex_txt(sp, tiq, 0, flags))
25219304Speter		return (1);
25319304Speter
254254225Speter	TAILQ_FOREACH(tp, tiq, q) {
25519304Speter		if (db_append(sp, 1, lno++, tp->lb, tp->len))
25619304Speter			return (1);
257254225Speter		++cnt;
258254225Speter	}
25919304Speter
26019304Speter	/*
26119304Speter	 * Set sp->lno to the final line number value (correcting for a
26219304Speter	 * possible 0 value) as that's historically correct for the final
26319304Speter	 * line value, whether or not the user entered any text.
26419304Speter	 */
26519304Speter	if ((sp->lno = lno) == 0 && db_exist(sp, 1))
26619304Speter		sp->lno = 1;
26719304Speter
26819304Speter	return (0);
26919304Speter}
270