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