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 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 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 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 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