119304Speter/*- 219304Speter * Copyright (c) 1992, 1993, 1994 319304Speter * The Regents of the University of California. All rights reserved. 419304Speter * Copyright (c) 1992, 1993, 1994, 1995, 1996 519304Speter * Keith Bostic. All rights reserved. 619304Speter * 719304Speter * See the LICENSE file for redistribution information. 819304Speter */ 919304Speter 1019304Speter#include "config.h" 1119304Speter 1219304Speter#ifndef lint 13254225Speterstatic const char sccsid[] = "$Id: ex_append.c,v 10.34 2001/06/25 15:19:14 skimo Exp $"; 1419304Speter#endif /* not lint */ 1519304Speter 1619304Speter#include <sys/types.h> 1719304Speter#include <sys/queue.h> 18254225Speter#include <sys/time.h> 1919304Speter 2019304Speter#include <bitstring.h> 2119304Speter#include <limits.h> 2219304Speter#include <stdio.h> 2319304Speter#include <string.h> 2419304Speter#include <unistd.h> 2519304Speter 2619304Speter#include "../common/common.h" 2719304Speter 2819304Speterenum which {APPEND, CHANGE, INSERT}; 2919304Speter 3019304Speterstatic int ex_aci __P((SCR *, EXCMD *, enum which)); 3119304Speter 3219304Speter/* 3319304Speter * ex_append -- :[line] a[ppend][!] 3419304Speter * Append one or more lines of new text after the specified line, 3519304Speter * or the current line if no address is specified. 3619304Speter * 3719304Speter * PUBLIC: int ex_append __P((SCR *, EXCMD *)); 3819304Speter */ 3919304Speterint 40254225Speterex_append(SCR *sp, EXCMD *cmdp) 4119304Speter{ 4219304Speter return (ex_aci(sp, cmdp, APPEND)); 4319304Speter} 4419304Speter 4519304Speter/* 4619304Speter * ex_change -- :[line[,line]] c[hange][!] [count] 4719304Speter * Change one or more lines to the input text. 4819304Speter * 4919304Speter * PUBLIC: int ex_change __P((SCR *, EXCMD *)); 5019304Speter */ 5119304Speterint 52254225Speterex_change(SCR *sp, EXCMD *cmdp) 5319304Speter{ 5419304Speter return (ex_aci(sp, cmdp, CHANGE)); 5519304Speter} 5619304Speter 5719304Speter/* 5819304Speter * ex_insert -- :[line] i[nsert][!] 5919304Speter * Insert one or more lines of new text before the specified line, 6019304Speter * or the current line if no address is specified. 6119304Speter * 6219304Speter * PUBLIC: int ex_insert __P((SCR *, EXCMD *)); 6319304Speter */ 6419304Speterint 65254225Speterex_insert(SCR *sp, EXCMD *cmdp) 6619304Speter{ 6719304Speter return (ex_aci(sp, cmdp, INSERT)); 6819304Speter} 6919304Speter 7019304Speter/* 7119304Speter * ex_aci -- 7219304Speter * Append, change, insert in ex. 7319304Speter */ 7419304Speterstatic int 75254225Speterex_aci(SCR *sp, EXCMD *cmdp, enum which cmd) 7619304Speter{ 7719304Speter CHAR_T *p, *t; 7819304Speter GS *gp; 7919304Speter TEXT *tp; 80254225Speter TEXTH tiq[] = {{ 0 }}; 81254225Speter recno_t cnt = 0, lno; 8219304Speter size_t len; 8319304Speter u_int32_t flags; 8419304Speter int need_newline; 8519304Speter 8619304Speter gp = sp->gp; 8719304Speter NEEDFILE(sp, cmdp); 8819304Speter 8919304Speter /* 9019304Speter * If doing a change, replace lines for as long as possible. Then, 9119304Speter * append more lines or delete remaining lines. Changes to an empty 9219304Speter * file are appends, inserts are the same as appends to the previous 9319304Speter * line. 9419304Speter * 9519304Speter * !!! 9619304Speter * Set the address to which we'll append. We set sp->lno to this 9719304Speter * address as well so that autoindent works correctly when get text 9819304Speter * from the user. 9919304Speter */ 10019304Speter lno = cmdp->addr1.lno; 10119304Speter sp->lno = lno; 10219304Speter if ((cmd == CHANGE || cmd == INSERT) && lno != 0) 10319304Speter --lno; 10419304Speter 10519304Speter /* 10619304Speter * !!! 10719304Speter * If the file isn't empty, cut changes into the unnamed buffer. 10819304Speter */ 10919304Speter if (cmd == CHANGE && cmdp->addr1.lno != 0 && 11019304Speter (cut(sp, NULL, &cmdp->addr1, &cmdp->addr2, CUT_LINEMODE) || 11119304Speter del(sp, &cmdp->addr1, &cmdp->addr2, 1))) 11219304Speter return (1); 11319304Speter 11419304Speter /* 11519304Speter * !!! 11619304Speter * Anything that was left after the command separator becomes part 11719304Speter * of the inserted text. Apparently, it was common usage to enter: 11819304Speter * 11919304Speter * :g/pattern/append|stuff1 12019304Speter * 12119304Speter * and append the line of text "stuff1" to the lines containing the 12219304Speter * pattern. It was also historically legal to enter: 12319304Speter * 12419304Speter * :append|stuff1 12519304Speter * stuff2 12619304Speter * . 12719304Speter * 12819304Speter * and the text on the ex command line would be appended as well as 12919304Speter * the text inserted after it. There was an historic bug however, 13019304Speter * that the user had to enter *two* terminating lines (the '.' lines) 13119304Speter * to terminate text input mode, in this case. This whole thing 13219304Speter * could be taken too far, however. Entering: 13319304Speter * 13419304Speter * :append|stuff1\ 13519304Speter * stuff2 13619304Speter * stuff3 13719304Speter * . 13819304Speter * 13919304Speter * i.e. mixing and matching the forms confused the historic vi, and, 14019304Speter * not only did it take two terminating lines to terminate text input 14119304Speter * mode, but the trailing backslashes were retained on the input. We 14219304Speter * match historic practice except that we discard the backslashes. 14319304Speter * 14419304Speter * Input lines specified on the ex command line lines are separated by 14519304Speter * <newline>s. If there is a trailing delimiter an empty line was 14619304Speter * inserted. There may also be a leading delimiter, which is ignored 14719304Speter * unless it's also a trailing delimiter. It is possible to encounter 14819304Speter * a termination line, i.e. a single '.', in a global command, but not 14919304Speter * necessary if the text insert command was the last of the global 15019304Speter * commands. 15119304Speter */ 15219304Speter if (cmdp->save_cmdlen != 0) { 15319304Speter for (p = cmdp->save_cmd, 15419304Speter len = cmdp->save_cmdlen; len > 0; p = t) { 15519304Speter for (t = p; len > 0 && t[0] != '\n'; ++t, --len); 15619304Speter if (t != p || len == 0) { 15719304Speter if (F_ISSET(sp, SC_EX_GLOBAL) && 15819304Speter t - p == 1 && p[0] == '.') { 15919304Speter ++t; 16019304Speter if (len > 0) 16119304Speter --len; 16219304Speter break; 16319304Speter } 16419304Speter if (db_append(sp, 1, lno++, p, t - p)) 16519304Speter return (1); 16619304Speter } 16719304Speter if (len != 0) { 16819304Speter ++t; 16919304Speter if (--len == 0 && 170254225Speter db_append(sp, 1, lno++, NULL, 0)) 17119304Speter return (1); 17219304Speter } 17319304Speter } 17419304Speter /* 17519304Speter * If there's any remaining text, we're in a global, and 17619304Speter * there's more command to parse. 17719304Speter * 17819304Speter * !!! 17919304Speter * We depend on the fact that non-global commands will eat the 18019304Speter * rest of the command line as text input, and before getting 18119304Speter * any text input from the user. Otherwise, we'd have to save 18219304Speter * off the command text before or during the call to the text 18319304Speter * input function below. 18419304Speter */ 18519304Speter if (len != 0) 18619304Speter cmdp->save_cmd = t; 18719304Speter cmdp->save_cmdlen = len; 18819304Speter } 18919304Speter 19019304Speter if (F_ISSET(sp, SC_EX_GLOBAL)) { 19119304Speter if ((sp->lno = lno) == 0 && db_exist(sp, 1)) 19219304Speter sp->lno = 1; 19319304Speter return (0); 19419304Speter } 19519304Speter 19619304Speter /* 19719304Speter * If not in a global command, read from the terminal. 19819304Speter * 19919304Speter * If this code is called by vi, we want to reset the terminal and use 20019304Speter * ex's line get routine. It actually works fine if we use vi's get 20119304Speter * routine, but it doesn't look as nice. Maybe if we had a separate 20219304Speter * window or something, but getting a line at a time looks awkward. 20319304Speter * However, depending on the screen that we're using, that may not 20419304Speter * be possible. 20519304Speter */ 20619304Speter if (F_ISSET(sp, SC_VI)) { 20719304Speter if (gp->scr_screen(sp, SC_EX)) { 208254225Speter ex_wemsg(sp, cmdp->cmd->name, EXM_NOCANON); 20919304Speter return (1); 21019304Speter } 21119304Speter 21219304Speter /* If we're still in the vi screen, move out explicitly. */ 21319304Speter need_newline = !F_ISSET(sp, SC_SCR_EXWROTE); 21419304Speter F_SET(sp, SC_SCR_EX | SC_SCR_EXWROTE); 21519304Speter if (need_newline) 21619304Speter (void)ex_puts(sp, "\n"); 21719304Speter 21819304Speter /* 21919304Speter * !!! 22019304Speter * Users of historical versions of vi sometimes get confused 22119304Speter * when they enter append mode, and can't seem to get out of 22219304Speter * it. Give them an informational message. 22319304Speter */ 22419304Speter (void)ex_puts(sp, 22519304Speter msg_cat(sp, "273|Entering ex input mode.", NULL)); 22619304Speter (void)ex_puts(sp, "\n"); 22719304Speter (void)ex_fflush(sp); 22819304Speter } 22919304Speter 23019304Speter /* 23119304Speter * Set input flags; the ! flag turns off autoindent for append, 23219304Speter * change and insert. 23319304Speter */ 23419304Speter LF_INIT(TXT_DOTTERM | TXT_NUMBER); 23519304Speter if (!FL_ISSET(cmdp->iflags, E_C_FORCE) && O_ISSET(sp, O_AUTOINDENT)) 23619304Speter LF_SET(TXT_AUTOINDENT); 23719304Speter if (O_ISSET(sp, O_BEAUTIFY)) 23819304Speter LF_SET(TXT_BEAUTIFY); 23919304Speter 24019304Speter /* 24119304Speter * This code can't use the common screen TEXTH structure (sp->tiq), 24219304Speter * as it may already be in use, e.g. ":append|s/abc/ABC/" would fail 24319304Speter * as we are only halfway through the text when the append code fires. 24419304Speter * Use a local structure instead. (The ex code would have to use a 24519304Speter * local structure except that we're guaranteed to finish remaining 24619304Speter * characters in the common TEXTH structure when they were inserted 24719304Speter * into the file, above.) 24819304Speter */ 249254225Speter TAILQ_INIT(tiq); 25019304Speter 251254225Speter if (ex_txt(sp, tiq, 0, flags)) 25219304Speter return (1); 25319304Speter 254254225Speter TAILQ_FOREACH(tp, tiq, q) { 25519304Speter if (db_append(sp, 1, lno++, tp->lb, tp->len)) 25619304Speter return (1); 257254225Speter ++cnt; 258254225Speter } 25919304Speter 26019304Speter /* 26119304Speter * Set sp->lno to the final line number value (correcting for a 26219304Speter * possible 0 value) as that's historically correct for the final 26319304Speter * line value, whether or not the user entered any text. 26419304Speter */ 26519304Speter if ((sp->lno = lno) == 0 && db_exist(sp, 1)) 26619304Speter sp->lno = 1; 26719304Speter 26819304Speter return (0); 26919304Speter} 270