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