xref: /minix3/external/bsd/nvi/dist/ex/ex_append.c (revision 0a6a1f1d05b60e214de2f05a7310ddd1f0e590e7)
1*0a6a1f1dSLionel Sambuc /*	$NetBSD: ex_append.c,v 1.4 2014/01/26 21:43:45 christos Exp $ */
284d9c625SLionel Sambuc /*-
384d9c625SLionel Sambuc  * Copyright (c) 1992, 1993, 1994
484d9c625SLionel Sambuc  *	The Regents of the University of California.  All rights reserved.
584d9c625SLionel Sambuc  * Copyright (c) 1992, 1993, 1994, 1995, 1996
684d9c625SLionel Sambuc  *	Keith Bostic.  All rights reserved.
784d9c625SLionel Sambuc  *
884d9c625SLionel Sambuc  * See the LICENSE file for redistribution information.
984d9c625SLionel Sambuc  */
1084d9c625SLionel Sambuc 
1184d9c625SLionel Sambuc #include "config.h"
1284d9c625SLionel Sambuc 
13*0a6a1f1dSLionel Sambuc #include <sys/cdefs.h>
14*0a6a1f1dSLionel Sambuc #if 0
1584d9c625SLionel Sambuc #ifndef lint
1684d9c625SLionel Sambuc static const char sccsid[] = "Id: ex_append.c,v 10.34 2001/06/25 15:19:14 skimo Exp  (Berkeley) Date: 2001/06/25 15:19:14 ";
1784d9c625SLionel Sambuc #endif /* not lint */
18*0a6a1f1dSLionel Sambuc #else
19*0a6a1f1dSLionel Sambuc __RCSID("$NetBSD: ex_append.c,v 1.4 2014/01/26 21:43:45 christos Exp $");
20*0a6a1f1dSLionel Sambuc #endif
2184d9c625SLionel Sambuc 
2284d9c625SLionel Sambuc #include <sys/types.h>
2384d9c625SLionel Sambuc #include <sys/queue.h>
2484d9c625SLionel Sambuc 
2584d9c625SLionel Sambuc #include <bitstring.h>
2684d9c625SLionel Sambuc #include <limits.h>
2784d9c625SLionel Sambuc #include <stdio.h>
2884d9c625SLionel Sambuc #include <string.h>
2984d9c625SLionel Sambuc #include <unistd.h>
3084d9c625SLionel Sambuc 
3184d9c625SLionel Sambuc #include "../common/common.h"
3284d9c625SLionel Sambuc 
3384d9c625SLionel Sambuc enum which {APPEND, CHANGE, INSERT};
3484d9c625SLionel Sambuc 
3584d9c625SLionel Sambuc static int ex_aci __P((SCR *, EXCMD *, enum which));
3684d9c625SLionel Sambuc 
3784d9c625SLionel Sambuc /*
3884d9c625SLionel Sambuc  * ex_append -- :[line] a[ppend][!]
3984d9c625SLionel Sambuc  *	Append one or more lines of new text after the specified line,
4084d9c625SLionel Sambuc  *	or the current line if no address is specified.
4184d9c625SLionel Sambuc  *
4284d9c625SLionel Sambuc  * PUBLIC: int ex_append __P((SCR *, EXCMD *));
4384d9c625SLionel Sambuc  */
4484d9c625SLionel Sambuc int
ex_append(SCR * sp,EXCMD * cmdp)4584d9c625SLionel Sambuc ex_append(SCR *sp, EXCMD *cmdp)
4684d9c625SLionel Sambuc {
4784d9c625SLionel Sambuc 	return (ex_aci(sp, cmdp, APPEND));
4884d9c625SLionel Sambuc }
4984d9c625SLionel Sambuc 
5084d9c625SLionel Sambuc /*
5184d9c625SLionel Sambuc  * ex_change -- :[line[,line]] c[hange][!] [count]
5284d9c625SLionel Sambuc  *	Change one or more lines to the input text.
5384d9c625SLionel Sambuc  *
5484d9c625SLionel Sambuc  * PUBLIC: int ex_change __P((SCR *, EXCMD *));
5584d9c625SLionel Sambuc  */
5684d9c625SLionel Sambuc int
ex_change(SCR * sp,EXCMD * cmdp)5784d9c625SLionel Sambuc ex_change(SCR *sp, EXCMD *cmdp)
5884d9c625SLionel Sambuc {
5984d9c625SLionel Sambuc 	return (ex_aci(sp, cmdp, CHANGE));
6084d9c625SLionel Sambuc }
6184d9c625SLionel Sambuc 
6284d9c625SLionel Sambuc /*
6384d9c625SLionel Sambuc  * ex_insert -- :[line] i[nsert][!]
6484d9c625SLionel Sambuc  *	Insert one or more lines of new text before the specified line,
6584d9c625SLionel Sambuc  *	or the current line if no address is specified.
6684d9c625SLionel Sambuc  *
6784d9c625SLionel Sambuc  * PUBLIC: int ex_insert __P((SCR *, EXCMD *));
6884d9c625SLionel Sambuc  */
6984d9c625SLionel Sambuc int
ex_insert(SCR * sp,EXCMD * cmdp)7084d9c625SLionel Sambuc ex_insert(SCR *sp, EXCMD *cmdp)
7184d9c625SLionel Sambuc {
7284d9c625SLionel Sambuc 	return (ex_aci(sp, cmdp, INSERT));
7384d9c625SLionel Sambuc }
7484d9c625SLionel Sambuc 
7584d9c625SLionel Sambuc /*
7684d9c625SLionel Sambuc  * ex_aci --
7784d9c625SLionel Sambuc  *	Append, change, insert in ex.
7884d9c625SLionel Sambuc  */
7984d9c625SLionel Sambuc static int
ex_aci(SCR * sp,EXCMD * cmdp,enum which cmd)8084d9c625SLionel Sambuc ex_aci(SCR *sp, EXCMD *cmdp, enum which cmd)
8184d9c625SLionel Sambuc {
8284d9c625SLionel Sambuc 	CHAR_T *p, *t;
8384d9c625SLionel Sambuc 	GS *gp;
8484d9c625SLionel Sambuc 	TEXT *tp;
8584d9c625SLionel Sambuc 	TEXTH tiq;
8684d9c625SLionel Sambuc 	db_recno_t cnt, lno;
8784d9c625SLionel Sambuc 	size_t len;
8884d9c625SLionel Sambuc 	u_int32_t flags;
8984d9c625SLionel Sambuc 	int need_newline;
9084d9c625SLionel Sambuc 
9184d9c625SLionel Sambuc 	gp = sp->gp;
9284d9c625SLionel Sambuc 	NEEDFILE(sp, cmdp);
9384d9c625SLionel Sambuc 
9484d9c625SLionel Sambuc 	/*
9584d9c625SLionel Sambuc 	 * If doing a change, replace lines for as long as possible.  Then,
9684d9c625SLionel Sambuc 	 * append more lines or delete remaining lines.  Changes to an empty
9784d9c625SLionel Sambuc 	 * file are appends, inserts are the same as appends to the previous
9884d9c625SLionel Sambuc 	 * line.
9984d9c625SLionel Sambuc 	 *
10084d9c625SLionel Sambuc 	 * !!!
10184d9c625SLionel Sambuc 	 * Set the address to which we'll append.  We set sp->lno to this
10284d9c625SLionel Sambuc 	 * address as well so that autoindent works correctly when get text
10384d9c625SLionel Sambuc 	 * from the user.
10484d9c625SLionel Sambuc 	 */
10584d9c625SLionel Sambuc 	lno = cmdp->addr1.lno;
10684d9c625SLionel Sambuc 	sp->lno = lno;
10784d9c625SLionel Sambuc 	if ((cmd == CHANGE || cmd == INSERT) && lno != 0)
10884d9c625SLionel Sambuc 		--lno;
10984d9c625SLionel Sambuc 
11084d9c625SLionel Sambuc 	/*
11184d9c625SLionel Sambuc 	 * !!!
11284d9c625SLionel Sambuc 	 * If the file isn't empty, cut changes into the unnamed buffer.
11384d9c625SLionel Sambuc 	 */
11484d9c625SLionel Sambuc 	if (cmd == CHANGE && cmdp->addr1.lno != 0 &&
11584d9c625SLionel Sambuc 	    (cut(sp, NULL, &cmdp->addr1, &cmdp->addr2, CUT_LINEMODE) ||
11684d9c625SLionel Sambuc 	    del(sp, &cmdp->addr1, &cmdp->addr2, 1)))
11784d9c625SLionel Sambuc 		return (1);
11884d9c625SLionel Sambuc 
11984d9c625SLionel Sambuc 	/*
12084d9c625SLionel Sambuc 	 * !!!
12184d9c625SLionel Sambuc 	 * Anything that was left after the command separator becomes part
12284d9c625SLionel Sambuc 	 * of the inserted text.  Apparently, it was common usage to enter:
12384d9c625SLionel Sambuc 	 *
12484d9c625SLionel Sambuc 	 *	:g/pattern/append|stuff1
12584d9c625SLionel Sambuc 	 *
12684d9c625SLionel Sambuc 	 * and append the line of text "stuff1" to the lines containing the
12784d9c625SLionel Sambuc 	 * pattern.  It was also historically legal to enter:
12884d9c625SLionel Sambuc 	 *
12984d9c625SLionel Sambuc 	 *	:append|stuff1
13084d9c625SLionel Sambuc 	 *	stuff2
13184d9c625SLionel Sambuc 	 *	.
13284d9c625SLionel Sambuc 	 *
13384d9c625SLionel Sambuc 	 * and the text on the ex command line would be appended as well as
13484d9c625SLionel Sambuc 	 * the text inserted after it.  There was an historic bug however,
13584d9c625SLionel Sambuc 	 * that the user had to enter *two* terminating lines (the '.' lines)
13684d9c625SLionel Sambuc 	 * to terminate text input mode, in this case.  This whole thing
13784d9c625SLionel Sambuc 	 * could be taken too far, however.  Entering:
13884d9c625SLionel Sambuc 	 *
13984d9c625SLionel Sambuc 	 *	:append|stuff1\
14084d9c625SLionel Sambuc 	 *	stuff2
14184d9c625SLionel Sambuc 	 *	stuff3
14284d9c625SLionel Sambuc 	 *	.
14384d9c625SLionel Sambuc 	 *
14484d9c625SLionel Sambuc 	 * i.e. mixing and matching the forms confused the historic vi, and,
14584d9c625SLionel Sambuc 	 * not only did it take two terminating lines to terminate text input
14684d9c625SLionel Sambuc 	 * mode, but the trailing backslashes were retained on the input.  We
14784d9c625SLionel Sambuc 	 * match historic practice except that we discard the backslashes.
14884d9c625SLionel Sambuc 	 *
14984d9c625SLionel Sambuc 	 * Input lines specified on the ex command line lines are separated by
15084d9c625SLionel Sambuc 	 * <newline>s.  If there is a trailing delimiter an empty line was
15184d9c625SLionel Sambuc 	 * inserted.  There may also be a leading delimiter, which is ignored
15284d9c625SLionel Sambuc 	 * unless it's also a trailing delimiter.  It is possible to encounter
15384d9c625SLionel Sambuc 	 * a termination line, i.e. a single '.', in a global command, but not
15484d9c625SLionel Sambuc 	 * necessary if the text insert command was the last of the global
15584d9c625SLionel Sambuc 	 * commands.
15684d9c625SLionel Sambuc 	 */
15784d9c625SLionel Sambuc 	if (cmdp->save_cmdlen != 0) {
15884d9c625SLionel Sambuc 		for (p = cmdp->save_cmd,
15984d9c625SLionel Sambuc 		    len = cmdp->save_cmdlen; len > 0; p = t) {
16084d9c625SLionel Sambuc 			for (t = p; len > 0 && t[0] != '\n'; ++t, --len);
16184d9c625SLionel Sambuc 			if (t != p || len == 0) {
16284d9c625SLionel Sambuc 				if (F_ISSET(sp, SC_EX_GLOBAL) &&
16384d9c625SLionel Sambuc 				    t - p == 1 && p[0] == '.') {
16484d9c625SLionel Sambuc 					++t;
16584d9c625SLionel Sambuc 					if (len > 0)
16684d9c625SLionel Sambuc 						--len;
16784d9c625SLionel Sambuc 					break;
16884d9c625SLionel Sambuc 				}
16984d9c625SLionel Sambuc 				if (db_append(sp, 1, lno++, p, t - p))
17084d9c625SLionel Sambuc 					return (1);
17184d9c625SLionel Sambuc 			}
17284d9c625SLionel Sambuc 			if (len != 0) {
17384d9c625SLionel Sambuc 				++t;
17484d9c625SLionel Sambuc 				if (--len == 0 &&
17584d9c625SLionel Sambuc 				    db_append(sp, 1, lno++, NULL, 0))
17684d9c625SLionel Sambuc 					return (1);
17784d9c625SLionel Sambuc 			}
17884d9c625SLionel Sambuc 		}
17984d9c625SLionel Sambuc 		/*
18084d9c625SLionel Sambuc 		 * If there's any remaining text, we're in a global, and
18184d9c625SLionel Sambuc 		 * there's more command to parse.
18284d9c625SLionel Sambuc 		 *
18384d9c625SLionel Sambuc 		 * !!!
18484d9c625SLionel Sambuc 		 * We depend on the fact that non-global commands will eat the
18584d9c625SLionel Sambuc 		 * rest of the command line as text input, and before getting
18684d9c625SLionel Sambuc 		 * any text input from the user.  Otherwise, we'd have to save
18784d9c625SLionel Sambuc 		 * off the command text before or during the call to the text
18884d9c625SLionel Sambuc 		 * input function below.
18984d9c625SLionel Sambuc 		 */
19084d9c625SLionel Sambuc 		if (len != 0)
19184d9c625SLionel Sambuc 			cmdp->save_cmd = t;
19284d9c625SLionel Sambuc 		cmdp->save_cmdlen = len;
19384d9c625SLionel Sambuc 	}
19484d9c625SLionel Sambuc 
19584d9c625SLionel Sambuc 	if (F_ISSET(sp, SC_EX_GLOBAL)) {
19684d9c625SLionel Sambuc 		if ((sp->lno = lno) == 0 && db_exist(sp, 1))
19784d9c625SLionel Sambuc 			sp->lno = 1;
19884d9c625SLionel Sambuc 		return (0);
19984d9c625SLionel Sambuc 	}
20084d9c625SLionel Sambuc 
20184d9c625SLionel Sambuc 	/*
20284d9c625SLionel Sambuc 	 * If not in a global command, read from the terminal.
20384d9c625SLionel Sambuc 	 *
20484d9c625SLionel Sambuc 	 * If this code is called by vi, we want to reset the terminal and use
20584d9c625SLionel Sambuc 	 * ex's line get routine.  It actually works fine if we use vi's get
20684d9c625SLionel Sambuc 	 * routine, but it doesn't look as nice.  Maybe if we had a separate
20784d9c625SLionel Sambuc 	 * window or something, but getting a line at a time looks awkward.
20884d9c625SLionel Sambuc 	 * However, depending on the screen that we're using, that may not
20984d9c625SLionel Sambuc 	 * be possible.
21084d9c625SLionel Sambuc 	 */
21184d9c625SLionel Sambuc 	if (F_ISSET(sp, SC_VI)) {
21284d9c625SLionel Sambuc 		if (gp->scr_screen(sp, SC_EX)) {
21384d9c625SLionel Sambuc 			ex_wemsg(sp, cmdp->cmd->name, EXM_NOCANON);
21484d9c625SLionel Sambuc 			return (1);
21584d9c625SLionel Sambuc 		}
21684d9c625SLionel Sambuc 
21784d9c625SLionel Sambuc 		/* If we're still in the vi screen, move out explicitly. */
21884d9c625SLionel Sambuc 		need_newline = !F_ISSET(sp, SC_SCR_EXWROTE);
21984d9c625SLionel Sambuc 		F_SET(sp, SC_SCR_EX | SC_SCR_EXWROTE);
22084d9c625SLionel Sambuc 		if (need_newline)
22184d9c625SLionel Sambuc 			(void)ex_puts(sp, "\n");
22284d9c625SLionel Sambuc 
22384d9c625SLionel Sambuc 		/*
22484d9c625SLionel Sambuc 		 * !!!
22584d9c625SLionel Sambuc 		 * Users of historical versions of vi sometimes get confused
22684d9c625SLionel Sambuc 		 * when they enter append mode, and can't seem to get out of
22784d9c625SLionel Sambuc 		 * it.  Give them an informational message.
22884d9c625SLionel Sambuc 		 */
22984d9c625SLionel Sambuc 		(void)ex_puts(sp,
23084d9c625SLionel Sambuc 		    msg_cat(sp, "273|Entering ex input mode.", NULL));
23184d9c625SLionel Sambuc 		(void)ex_puts(sp, "\n");
23284d9c625SLionel Sambuc 		(void)ex_fflush(sp);
23384d9c625SLionel Sambuc 	}
23484d9c625SLionel Sambuc 
23584d9c625SLionel Sambuc 	/*
23684d9c625SLionel Sambuc 	 * Set input flags; the ! flag turns off autoindent for append,
23784d9c625SLionel Sambuc 	 * change and insert.
23884d9c625SLionel Sambuc 	 */
23984d9c625SLionel Sambuc 	LF_INIT(TXT_DOTTERM | TXT_NUMBER);
24084d9c625SLionel Sambuc 	if (!FL_ISSET(cmdp->iflags, E_C_FORCE) && O_ISSET(sp, O_AUTOINDENT))
24184d9c625SLionel Sambuc 		LF_SET(TXT_AUTOINDENT);
24284d9c625SLionel Sambuc 	if (O_ISSET(sp, O_BEAUTIFY))
24384d9c625SLionel Sambuc 		LF_SET(TXT_BEAUTIFY);
24484d9c625SLionel Sambuc 
24584d9c625SLionel Sambuc 	/*
24684d9c625SLionel Sambuc 	 * This code can't use the common screen TEXTH structure (sp->tiq),
24784d9c625SLionel Sambuc 	 * as it may already be in use, e.g. ":append|s/abc/ABC/" would fail
24884d9c625SLionel Sambuc 	 * as we are only halfway through the text when the append code fires.
24984d9c625SLionel Sambuc 	 * Use a local structure instead.  (The ex code would have to use a
25084d9c625SLionel Sambuc 	 * local structure except that we're guaranteed to finish remaining
25184d9c625SLionel Sambuc 	 * characters in the common TEXTH structure when they were inserted
25284d9c625SLionel Sambuc 	 * into the file, above.)
25384d9c625SLionel Sambuc 	 */
25484d9c625SLionel Sambuc 	memset(&tiq, 0, sizeof(TEXTH));
25584d9c625SLionel Sambuc 	TAILQ_INIT(&tiq);
25684d9c625SLionel Sambuc 
25784d9c625SLionel Sambuc 	if (ex_txt(sp, &tiq, 0, flags))
25884d9c625SLionel Sambuc 		return (1);
25984d9c625SLionel Sambuc 
26084d9c625SLionel Sambuc 	for (cnt = 0, tp = TAILQ_FIRST(&tiq); tp != NULL;
26184d9c625SLionel Sambuc 	    ++cnt, tp = TAILQ_NEXT(tp, q))
26284d9c625SLionel Sambuc 		if (db_append(sp, 1, lno++, tp->lb, tp->len))
26384d9c625SLionel Sambuc 			return (1);
26484d9c625SLionel Sambuc 
26584d9c625SLionel Sambuc 	/*
26684d9c625SLionel Sambuc 	 * Set sp->lno to the final line number value (correcting for a
26784d9c625SLionel Sambuc 	 * possible 0 value) as that's historically correct for the final
26884d9c625SLionel Sambuc 	 * line value, whether or not the user entered any text.
26984d9c625SLionel Sambuc 	 */
27084d9c625SLionel Sambuc 	if ((sp->lno = lno) == 0 && db_exist(sp, 1))
27184d9c625SLionel Sambuc 		sp->lno = 1;
27284d9c625SLionel Sambuc 
27384d9c625SLionel Sambuc 	return (0);
27484d9c625SLionel Sambuc }
275