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