1 /*- 2 * Copyright (c) 1992, 1993, 1994 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by the University of 16 * California, Berkeley and its contributors. 17 * 4. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34 #ifndef lint 35 static char sccsid[] = "@(#)ex_write.c 8.38 (Berkeley) 8/17/94"; 36 #endif /* not lint */ 37 38 #include <sys/types.h> 39 #include <sys/queue.h> 40 #include <sys/stat.h> 41 #include <sys/time.h> 42 43 #include <bitstring.h> 44 #include <ctype.h> 45 #include <errno.h> 46 #include <fcntl.h> 47 #include <limits.h> 48 #include <signal.h> 49 #include <stdio.h> 50 #include <string.h> 51 #include <termios.h> 52 #include <unistd.h> 53 54 #include "compat.h" 55 #include <db.h> 56 #include <regex.h> 57 58 #include "vi.h" 59 #include "excmd.h" 60 61 enum which {WN, WQ, WRITE, XIT}; 62 63 static int exwr __P((SCR *, EXF *, EXCMDARG *, enum which)); 64 65 /* 66 * ex_wn -- :wn[!] [>>] [file] 67 * Write to a file and switch to the next one. 68 */ 69 int 70 ex_wn(sp, ep, cmdp) 71 SCR *sp; 72 EXF *ep; 73 EXCMDARG *cmdp; 74 { 75 if (exwr(sp, ep, cmdp, WN)) 76 return (1); 77 if (file_m3(sp, ep, 0)) 78 return (1); 79 80 /* The file name isn't a new file to edit. */ 81 cmdp->argc = 0; 82 83 return (ex_next(sp, ep, cmdp)); 84 } 85 86 /* 87 * ex_wq -- :wq[!] [>>] [file] 88 * Write to a file and quit. 89 */ 90 int 91 ex_wq(sp, ep, cmdp) 92 SCR *sp; 93 EXF *ep; 94 EXCMDARG *cmdp; 95 { 96 int force; 97 98 if (exwr(sp, ep, cmdp, WQ)) 99 return (1); 100 if (file_m3(sp, ep, 0)) 101 return (1); 102 103 force = F_ISSET(cmdp, E_FORCE); 104 105 if (ex_ncheck(sp, force)) 106 return (1); 107 108 F_SET(sp, force ? S_EXIT_FORCE : S_EXIT); 109 return (0); 110 } 111 112 /* 113 * ex_write -- :write[!] [>>] [file] 114 * :write [!] [cmd] 115 * Write to a file. 116 */ 117 int 118 ex_write(sp, ep, cmdp) 119 SCR *sp; 120 EXF *ep; 121 EXCMDARG *cmdp; 122 { 123 return (exwr(sp, ep, cmdp, WRITE)); 124 } 125 126 127 /* 128 * ex_xit -- :x[it]! [file] 129 * 130 * Write out any modifications and quit. 131 */ 132 int 133 ex_xit(sp, ep, cmdp) 134 SCR *sp; 135 EXF *ep; 136 EXCMDARG *cmdp; 137 { 138 int force; 139 140 if (F_ISSET((ep), F_MODIFIED) && exwr(sp, ep, cmdp, XIT)) 141 return (1); 142 if (file_m3(sp, ep, 0)) 143 return (1); 144 145 force = F_ISSET(cmdp, E_FORCE); 146 147 if (ex_ncheck(sp, force)) 148 return (1); 149 150 F_SET(sp, force ? S_EXIT_FORCE : S_EXIT); 151 return (0); 152 } 153 154 /* 155 * exwr -- 156 * The guts of the ex write commands. 157 */ 158 static int 159 exwr(sp, ep, cmdp, cmd) 160 SCR *sp; 161 EXF *ep; 162 EXCMDARG *cmdp; 163 enum which cmd; 164 { 165 EX_PRIVATE *exp; 166 MARK rm; 167 int flags; 168 char *name, *p; 169 170 /* All write commands can have an associated '!'. */ 171 LF_INIT(FS_POSSIBLE); 172 if (F_ISSET(cmdp, E_FORCE)) 173 LF_SET(FS_FORCE); 174 175 /* Skip any leading whitespace. */ 176 if (cmdp->argc != 0) 177 for (p = cmdp->argv[0]->bp; *p && isblank(*p); ++p); 178 179 /* If no arguments, just write the file back. */ 180 if (cmdp->argc == 0 || *p == '\0') { 181 if (F_ISSET(cmdp, E_ADDR2_ALL)) 182 LF_SET(FS_ALL); 183 return (file_write(sp, ep, 184 &cmdp->addr1, &cmdp->addr2, NULL, flags)); 185 } 186 187 /* If "write !" it's a pipe to a utility. */ 188 exp = EXP(sp); 189 if (cmd == WRITE && *p == '!') { 190 for (++p; *p && isblank(*p); ++p); 191 if (*p == '\0') { 192 msgq(sp, M_ERR, "Usage: %s", cmdp->cmd->usage); 193 return (1); 194 } 195 /* Expand the argument. */ 196 if (argv_exp1(sp, ep, cmdp, p, strlen(p), 0)) 197 return (1); 198 if (filtercmd(sp, ep, &cmdp->addr1, &cmdp->addr2, 199 &rm, cmdp->argv[1]->bp, FILTER_WRITE)) 200 return (1); 201 sp->lno = rm.lno; 202 return (0); 203 } 204 205 /* If "write >>" it's an append to a file. */ 206 if (cmd != XIT && p[0] == '>' && p[1] == '>') { 207 LF_SET(FS_APPEND); 208 209 /* Skip ">>" and whitespace. */ 210 for (p += 2; *p && isblank(*p); ++p); 211 } 212 213 /* Build an argv so we get an argument count and file expansion. */ 214 if (argv_exp2(sp, ep, cmdp, p, strlen(p), 0)) 215 return (1); 216 217 switch (cmdp->argc) { 218 case 1: 219 /* 220 * Nothing to expand, write the current file. 221 * XXX 222 * Should never happen, already checked this case. 223 */ 224 name = NULL; 225 break; 226 case 2: 227 /* One new argument, write it. */ 228 name = cmdp->argv[exp->argsoff - 1]->bp; 229 set_alt_name(sp, name); 230 break; 231 default: 232 /* If expanded to more than one argument, object. */ 233 msgq(sp, M_ERR, "%s expanded into too many file names", 234 cmdp->argv[0]->bp); 235 msgq(sp, M_ERR, "Usage: %s", cmdp->cmd->usage); 236 return (1); 237 } 238 239 if (F_ISSET(cmdp, E_ADDR2_ALL)) 240 LF_SET(FS_ALL); 241 return (file_write(sp, ep, &cmdp->addr1, &cmdp->addr2, name, flags)); 242 } 243 244 /* 245 * ex_writefp -- 246 * Write a range of lines to a FILE *. 247 */ 248 int 249 ex_writefp(sp, ep, name, fp, fm, tm, nlno, nch) 250 SCR *sp; 251 EXF *ep; 252 char *name; 253 FILE *fp; 254 MARK *fm, *tm; 255 u_long *nlno, *nch; 256 { 257 struct stat sb; 258 u_long ccnt; /* XXX: can't print off_t portably. */ 259 recno_t fline, tline, lcnt; 260 size_t len; 261 int sv_errno; 262 char *p; 263 264 fline = fm->lno; 265 tline = tm->lno; 266 267 if (nlno != NULL) { 268 *nch = 0; 269 *nlno = 0; 270 } 271 272 /* 273 * The vi filter code has multiple processes running simultaneously, 274 * and one of them calls ex_writefp(). The "unsafe" function calls 275 * in this code are to file_gline() and msgq(). File_gline() is safe, 276 * see the comment in filter.c:filtercmd() for details. We don't call 277 * msgq if the multiple process bit in the EXF is set. 278 * 279 * !!! 280 * Historic vi permitted files of 0 length to be written. However, 281 * since the way vi got around dealing with "empty" files was to 282 * always have a line in the file no matter what, it wrote them as 283 * files of a single, empty line. We write empty files. 284 * 285 * "Alex, I'll take vi trivia for $1000." 286 */ 287 ccnt = 0; 288 lcnt = 0; 289 if (tline != 0) { 290 for (; fline <= tline; ++fline, ++lcnt) { 291 /* Caller has to provide any interrupt message. */ 292 if (INTERRUPTED(sp)) 293 break; 294 if ((p = file_gline(sp, ep, fline, &len)) == NULL) 295 break; 296 if (fwrite(p, 1, len, fp) != len) { 297 msgq(sp, M_SYSERR, name); 298 (void)fclose(fp); 299 return (1); 300 } 301 ccnt += len; 302 if (putc('\n', fp) != '\n') 303 break; 304 ++ccnt; 305 } 306 } 307 308 /* If it's a regular file, sync it so that NFS is forced to flush. */ 309 if (!fstat(fileno(fp), &sb) && 310 S_ISREG(sb.st_mode) && fsync(fileno(fp))) { 311 sv_errno = errno; 312 (void)fclose(fp); 313 errno = sv_errno; 314 goto err; 315 } 316 if (fclose(fp)) 317 goto err; 318 if (nlno != NULL) { 319 *nch = ccnt; 320 *nlno = lcnt; 321 } 322 return (0); 323 324 err: if (!F_ISSET(ep, F_MULTILOCK)) 325 msgq(sp, M_SYSERR, name); 326 return (1); 327 } 328