1/*-
2 * Copyright (c) 1992, 1993, 1994
3 *	The Regents of the University of California.  All rights reserved.
4 * Copyright (c) 1992, 1993, 1994, 1995, 1996
5 *	Keith Bostic.  All rights reserved.
6 *
7 * See the LICENSE file for redistribution information.
8 */
9
10#include "config.h"
11
12#ifndef lint
13static const char sccsid[] = "$Id: ex_append.c,v 10.34 2001/06/25 15:19:14 skimo Exp $";
14#endif /* not lint */
15
16#include <sys/types.h>
17#include <sys/queue.h>
18#include <sys/time.h>
19
20#include <bitstring.h>
21#include <limits.h>
22#include <stdio.h>
23#include <string.h>
24#include <unistd.h>
25
26#include "../common/common.h"
27
28enum which {APPEND, CHANGE, INSERT};
29
30static int ex_aci __P((SCR *, EXCMD *, enum which));
31
32/*
33 * ex_append -- :[line] a[ppend][!]
34 *	Append one or more lines of new text after the specified line,
35 *	or the current line if no address is specified.
36 *
37 * PUBLIC: int ex_append __P((SCR *, EXCMD *));
38 */
39int
40ex_append(SCR *sp, EXCMD *cmdp)
41{
42	return (ex_aci(sp, cmdp, APPEND));
43}
44
45/*
46 * ex_change -- :[line[,line]] c[hange][!] [count]
47 *	Change one or more lines to the input text.
48 *
49 * PUBLIC: int ex_change __P((SCR *, EXCMD *));
50 */
51int
52ex_change(SCR *sp, EXCMD *cmdp)
53{
54	return (ex_aci(sp, cmdp, CHANGE));
55}
56
57/*
58 * ex_insert -- :[line] i[nsert][!]
59 *	Insert one or more lines of new text before the specified line,
60 *	or the current line if no address is specified.
61 *
62 * PUBLIC: int ex_insert __P((SCR *, EXCMD *));
63 */
64int
65ex_insert(SCR *sp, EXCMD *cmdp)
66{
67	return (ex_aci(sp, cmdp, INSERT));
68}
69
70/*
71 * ex_aci --
72 *	Append, change, insert in ex.
73 */
74static int
75ex_aci(SCR *sp, EXCMD *cmdp, enum which cmd)
76{
77	CHAR_T *p, *t;
78	GS *gp;
79	TEXT *tp;
80	TEXTH tiq[] = {{ 0 }};
81	recno_t cnt = 0, lno;
82	size_t len;
83	u_int32_t flags;
84	int need_newline;
85
86	gp = sp->gp;
87	NEEDFILE(sp, cmdp);
88
89	/*
90	 * If doing a change, replace lines for as long as possible.  Then,
91	 * append more lines or delete remaining lines.  Changes to an empty
92	 * file are appends, inserts are the same as appends to the previous
93	 * line.
94	 *
95	 * !!!
96	 * Set the address to which we'll append.  We set sp->lno to this
97	 * address as well so that autoindent works correctly when get text
98	 * from the user.
99	 */
100	lno = cmdp->addr1.lno;
101	sp->lno = lno;
102	if ((cmd == CHANGE || cmd == INSERT) && lno != 0)
103		--lno;
104
105	/*
106	 * !!!
107	 * If the file isn't empty, cut changes into the unnamed buffer.
108	 */
109	if (cmd == CHANGE && cmdp->addr1.lno != 0 &&
110	    (cut(sp, NULL, &cmdp->addr1, &cmdp->addr2, CUT_LINEMODE) ||
111	    del(sp, &cmdp->addr1, &cmdp->addr2, 1)))
112		return (1);
113
114	/*
115	 * !!!
116	 * Anything that was left after the command separator becomes part
117	 * of the inserted text.  Apparently, it was common usage to enter:
118	 *
119	 *	:g/pattern/append|stuff1
120	 *
121	 * and append the line of text "stuff1" to the lines containing the
122	 * pattern.  It was also historically legal to enter:
123	 *
124	 *	:append|stuff1
125	 *	stuff2
126	 *	.
127	 *
128	 * and the text on the ex command line would be appended as well as
129	 * the text inserted after it.  There was an historic bug however,
130	 * that the user had to enter *two* terminating lines (the '.' lines)
131	 * to terminate text input mode, in this case.  This whole thing
132	 * could be taken too far, however.  Entering:
133	 *
134	 *	:append|stuff1\
135	 *	stuff2
136	 *	stuff3
137	 *	.
138	 *
139	 * i.e. mixing and matching the forms confused the historic vi, and,
140	 * not only did it take two terminating lines to terminate text input
141	 * mode, but the trailing backslashes were retained on the input.  We
142	 * match historic practice except that we discard the backslashes.
143	 *
144	 * Input lines specified on the ex command line lines are separated by
145	 * <newline>s.  If there is a trailing delimiter an empty line was
146	 * inserted.  There may also be a leading delimiter, which is ignored
147	 * unless it's also a trailing delimiter.  It is possible to encounter
148	 * a termination line, i.e. a single '.', in a global command, but not
149	 * necessary if the text insert command was the last of the global
150	 * commands.
151	 */
152	if (cmdp->save_cmdlen != 0) {
153		for (p = cmdp->save_cmd,
154		    len = cmdp->save_cmdlen; len > 0; p = t) {
155			for (t = p; len > 0 && t[0] != '\n'; ++t, --len);
156			if (t != p || len == 0) {
157				if (F_ISSET(sp, SC_EX_GLOBAL) &&
158				    t - p == 1 && p[0] == '.') {
159					++t;
160					if (len > 0)
161						--len;
162					break;
163				}
164				if (db_append(sp, 1, lno++, p, t - p))
165					return (1);
166			}
167			if (len != 0) {
168				++t;
169				if (--len == 0 &&
170				    db_append(sp, 1, lno++, NULL, 0))
171					return (1);
172			}
173		}
174		/*
175		 * If there's any remaining text, we're in a global, and
176		 * there's more command to parse.
177		 *
178		 * !!!
179		 * We depend on the fact that non-global commands will eat the
180		 * rest of the command line as text input, and before getting
181		 * any text input from the user.  Otherwise, we'd have to save
182		 * off the command text before or during the call to the text
183		 * input function below.
184		 */
185		if (len != 0)
186			cmdp->save_cmd = t;
187		cmdp->save_cmdlen = len;
188	}
189
190	if (F_ISSET(sp, SC_EX_GLOBAL)) {
191		if ((sp->lno = lno) == 0 && db_exist(sp, 1))
192			sp->lno = 1;
193		return (0);
194	}
195
196	/*
197	 * If not in a global command, read from the terminal.
198	 *
199	 * If this code is called by vi, we want to reset the terminal and use
200	 * ex's line get routine.  It actually works fine if we use vi's get
201	 * routine, but it doesn't look as nice.  Maybe if we had a separate
202	 * window or something, but getting a line at a time looks awkward.
203	 * However, depending on the screen that we're using, that may not
204	 * be possible.
205	 */
206	if (F_ISSET(sp, SC_VI)) {
207		if (gp->scr_screen(sp, SC_EX)) {
208			ex_wemsg(sp, cmdp->cmd->name, EXM_NOCANON);
209			return (1);
210		}
211
212		/* If we're still in the vi screen, move out explicitly. */
213		need_newline = !F_ISSET(sp, SC_SCR_EXWROTE);
214		F_SET(sp, SC_SCR_EX | SC_SCR_EXWROTE);
215		if (need_newline)
216			(void)ex_puts(sp, "\n");
217
218		/*
219		 * !!!
220		 * Users of historical versions of vi sometimes get confused
221		 * when they enter append mode, and can't seem to get out of
222		 * it.  Give them an informational message.
223		 */
224		(void)ex_puts(sp,
225		    msg_cat(sp, "273|Entering ex input mode.", NULL));
226		(void)ex_puts(sp, "\n");
227		(void)ex_fflush(sp);
228	}
229
230	/*
231	 * Set input flags; the ! flag turns off autoindent for append,
232	 * change and insert.
233	 */
234	LF_INIT(TXT_DOTTERM | TXT_NUMBER);
235	if (!FL_ISSET(cmdp->iflags, E_C_FORCE) && O_ISSET(sp, O_AUTOINDENT))
236		LF_SET(TXT_AUTOINDENT);
237	if (O_ISSET(sp, O_BEAUTIFY))
238		LF_SET(TXT_BEAUTIFY);
239
240	/*
241	 * This code can't use the common screen TEXTH structure (sp->tiq),
242	 * as it may already be in use, e.g. ":append|s/abc/ABC/" would fail
243	 * as we are only halfway through the text when the append code fires.
244	 * Use a local structure instead.  (The ex code would have to use a
245	 * local structure except that we're guaranteed to finish remaining
246	 * characters in the common TEXTH structure when they were inserted
247	 * into the file, above.)
248	 */
249	TAILQ_INIT(tiq);
250
251	if (ex_txt(sp, tiq, 0, flags))
252		return (1);
253
254	TAILQ_FOREACH(tp, tiq, q) {
255		if (db_append(sp, 1, lno++, tp->lb, tp->len))
256			return (1);
257		++cnt;
258	}
259
260	/*
261	 * Set sp->lno to the final line number value (correcting for a
262	 * possible 0 value) as that's historically correct for the final
263	 * line value, whether or not the user entered any text.
264	 */
265	if ((sp->lno = lno) == 0 && db_exist(sp, 1))
266		sp->lno = 1;
267
268	return (0);
269}
270