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