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