1/* $NetBSD: ex_bang.c,v 1.3 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 16static const char sccsid[] = "Id: ex_bang.c,v 10.36 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_bang.c,v 1.3 2014/01/26 21:43:45 christos Exp $"); 20#endif 21 22#include <sys/types.h> 23#include <sys/queue.h> 24#include <sys/time.h> 25 26#include <bitstring.h> 27#include <errno.h> 28#include <limits.h> 29#include <stdio.h> 30#include <stdlib.h> 31#include <string.h> 32#include <unistd.h> 33 34#include "../common/common.h" 35#include "../vi/vi.h" 36 37/* 38 * ex_bang -- :[line [,line]] ! command 39 * 40 * Pass the rest of the line after the ! character to the program named by 41 * the O_SHELL option. 42 * 43 * Historical vi did NOT do shell expansion on the arguments before passing 44 * them, only file name expansion. This means that the O_SHELL program got 45 * "$t" as an argument if that is what the user entered. Also, there's a 46 * special expansion done for the bang command. Any exclamation points in 47 * the user's argument are replaced by the last, expanded ! command. 48 * 49 * There's some fairly amazing slop in this routine to make the different 50 * ways of getting here display the right things. It took a long time to 51 * get it right (wrong?), so be careful. 52 * 53 * PUBLIC: int ex_bang __P((SCR *, EXCMD *)); 54 */ 55int 56ex_bang(SCR *sp, EXCMD *cmdp) 57{ 58 enum filtertype ftype; 59 ARGS *ap; 60 EX_PRIVATE *exp; 61 MARK rm; 62 db_recno_t lno; 63 const char *msg; 64 const char *np; 65 size_t nlen; 66 67 ap = cmdp->argv[0]; 68 if (ap->len == 0) { 69 ex_emsg(sp, cmdp->cmd->usage, EXM_USAGE); 70 return (1); 71 } 72 73 /* Set the "last bang command" remembered value. */ 74 exp = EXP(sp); 75 if (exp->lastbcomm != NULL) 76 free(exp->lastbcomm); 77 if ((exp->lastbcomm = v_wstrdup(sp, ap->bp, ap->len)) == NULL) { 78 msgq(sp, M_SYSERR, NULL); 79 return (1); 80 } 81 82 /* 83 * If the command was modified by the expansion, it was historically 84 * redisplayed. 85 */ 86 if (F_ISSET(cmdp, E_MODIFY) && !F_ISSET(sp, SC_EX_SILENT)) { 87 /* 88 * Display the command if modified. Historic ex/vi displayed 89 * the command if it was modified due to file name and/or bang 90 * expansion. If piping lines in vi, it would be immediately 91 * overwritten by any error or line change reporting. 92 */ 93 if (F_ISSET(sp, SC_VI)) 94 vs_update(sp, "!", ap->bp); 95 else { 96 INT2CHAR(sp, ap->bp, ap->len+1, np, nlen); 97 (void)ex_printf(sp, "!%s\n", np); 98 (void)ex_fflush(sp); 99 } 100 } 101 102 /* 103 * If no addresses were specified, run the command. If there's an 104 * underlying file, it's been modified and autowrite is set, write 105 * the file back. If the file has been modified, autowrite is not 106 * set and the warn option is set, tell the user about the file. 107 */ 108 if (cmdp->addrcnt == 0) { 109 msg = NULL; 110 if (sp->ep != NULL && F_ISSET(sp->ep, F_MODIFIED)) { 111 if (O_ISSET(sp, O_AUTOWRITE)) { 112 if (file_aw(sp, FS_ALL)) 113 return (0); 114 } else if (O_ISSET(sp, O_WARN) && 115 !F_ISSET(sp, SC_EX_SILENT)) 116 msg = msg_cat(sp, 117 "303|File modified since last write.", 118 NULL); 119 } 120 121 /* If we're still in a vi screen, move out explicitly. */ 122 INT2CHAR(sp, ap->bp, ap->len+1, np, nlen); 123 (void)ex_exec_proc(sp, 124 cmdp, np, msg, !F_ISSET(sp, SC_EX | SC_SCR_EXWROTE)); 125 } 126 127 /* 128 * If addresses were specified, pipe lines from the file through the 129 * command. 130 * 131 * Historically, vi lines were replaced by both the stdout and stderr 132 * lines of the command, but ex lines by only the stdout lines. This 133 * makes no sense to me, so nvi makes it consistent for both, and 134 * matches vi's historic behavior. 135 */ 136 else { 137 NEEDFILE(sp, cmdp); 138 139 /* Autoprint is set historically, even if the command fails. */ 140 F_SET(cmdp, E_AUTOPRINT); 141 142 /* 143 * !!! 144 * Historical vi permitted "!!" in an empty file. When this 145 * happens, we arrive here with two addresses of 1,1 and a 146 * bad attitude. The simple solution is to turn it into a 147 * FILTER_READ operation, with the exception that stdin isn't 148 * opened for the utility, and the cursor position isn't the 149 * same. The only historic glitch (I think) is that we don't 150 * put an empty line into the default cut buffer, as historic 151 * vi did. Imagine, if you can, my disappointment. 152 */ 153 ftype = FILTER_BANG; 154 if (cmdp->addr1.lno == 1 && cmdp->addr2.lno == 1) { 155 if (db_last(sp, &lno)) 156 return (1); 157 if (lno == 0) { 158 cmdp->addr1.lno = cmdp->addr2.lno = 0; 159 ftype = FILTER_RBANG; 160 } 161 } 162 (void)ex_filter(sp, cmdp, 163 &cmdp->addr1, &cmdp->addr2, &rm, ap->bp, ftype); 164 165 /* 166 * If in vi mode, move to the first nonblank. 167 * 168 * !!! 169 * Historic vi wasn't consistent in this area -- if you used 170 * a forward motion it moved to the first nonblank, but if you 171 * did a backward motion it didn't. And, if you followed a 172 * backward motion with a forward motion, it wouldn't move to 173 * the nonblank for either. Going to the nonblank generally 174 * seems more useful and consistent, so we do it. 175 */ 176 sp->lno = rm.lno; 177 if (F_ISSET(sp, SC_VI)) { 178 sp->cno = 0; 179 (void)nonblank(sp, sp->lno, &sp->cno); 180 } else 181 sp->cno = rm.cno; 182 } 183 184 /* Ex terminates with a bang, even if the command fails. */ 185 if (!F_ISSET(sp, SC_VI) && !F_ISSET(sp, SC_EX_SILENT)) 186 (void)ex_puts(sp, "!\n"); 187 188 /* 189 * XXX 190 * The ! commands never return an error, so that autoprint always 191 * happens in the ex parser. 192 */ 193 return (0); 194} 195