1 /* $NetBSD: ex_write.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 16 static const char sccsid[] = "Id: ex_write.c,v 10.38 2001/06/25 15:19:22 skimo Exp (Berkeley) Date: 2001/06/25 15:19:22 "; 17 #endif /* not lint */ 18 #else 19 __RCSID("$NetBSD: ex_write.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/stat.h> 25 26 #include <bitstring.h> 27 #include <ctype.h> 28 #include <errno.h> 29 #include <fcntl.h> 30 #include <limits.h> 31 #include <stdio.h> 32 #include <stdlib.h> 33 #include <string.h> 34 #include <unistd.h> 35 36 #include "../common/common.h" 37 38 enum which {WN, WQ, WRITE, XIT}; 39 static int exwr __P((SCR *, EXCMD *, enum which)); 40 41 /* 42 * ex_wn -- :wn[!] [>>] [file] 43 * Write to a file and switch to the next one. 44 * 45 * PUBLIC: int ex_wn __P((SCR *, EXCMD *)); 46 */ 47 int 48 ex_wn(SCR *sp, EXCMD *cmdp) 49 { 50 if (exwr(sp, cmdp, WN)) 51 return (1); 52 if (file_m3(sp, 0)) 53 return (1); 54 55 /* The file name isn't a new file to edit. */ 56 cmdp->argc = 0; 57 58 return (ex_next(sp, cmdp)); 59 } 60 61 /* 62 * ex_wq -- :wq[!] [>>] [file] 63 * Write to a file and quit. 64 * 65 * PUBLIC: int ex_wq __P((SCR *, EXCMD *)); 66 */ 67 int 68 ex_wq(SCR *sp, EXCMD *cmdp) 69 { 70 int force; 71 72 if (exwr(sp, cmdp, WQ)) 73 return (1); 74 if (file_m3(sp, 0)) 75 return (1); 76 77 force = FL_ISSET(cmdp->iflags, E_C_FORCE); 78 79 if (ex_ncheck(sp, force)) 80 return (1); 81 82 F_SET(sp, force ? SC_EXIT_FORCE : SC_EXIT); 83 return (0); 84 } 85 86 /* 87 * ex_write -- :write[!] [>>] [file] 88 * :write [!] [cmd] 89 * Write to a file. 90 * 91 * PUBLIC: int ex_write __P((SCR *, EXCMD *)); 92 */ 93 int 94 ex_write(SCR *sp, EXCMD *cmdp) 95 { 96 return (exwr(sp, cmdp, WRITE)); 97 } 98 99 100 /* 101 * ex_xit -- :x[it]! [file] 102 * Write out any modifications and quit. 103 * 104 * PUBLIC: int ex_xit __P((SCR *, EXCMD *)); 105 */ 106 int 107 ex_xit(SCR *sp, EXCMD *cmdp) 108 { 109 int force; 110 111 NEEDFILE(sp, cmdp); 112 113 if (F_ISSET(sp->ep, F_MODIFIED) && exwr(sp, cmdp, XIT)) 114 return (1); 115 if (file_m3(sp, 0)) 116 return (1); 117 118 force = FL_ISSET(cmdp->iflags, E_C_FORCE); 119 120 if (ex_ncheck(sp, force)) 121 return (1); 122 123 F_SET(sp, force ? SC_EXIT_FORCE : SC_EXIT); 124 return (0); 125 } 126 127 /* 128 * exwr -- 129 * The guts of the ex write commands. 130 */ 131 static int 132 exwr(SCR *sp, EXCMD *cmdp, enum which cmd) 133 { 134 MARK rm; 135 int flags; 136 char *name; 137 CHAR_T *p = NULL; 138 size_t nlen; 139 const char *n; 140 int rc; 141 EX_PRIVATE *exp; 142 143 NEEDFILE(sp, cmdp); 144 145 /* All write commands can have an associated '!'. */ 146 LF_INIT(FS_POSSIBLE); 147 if (FL_ISSET(cmdp->iflags, E_C_FORCE)) 148 LF_SET(FS_FORCE); 149 150 /* Skip any leading whitespace. */ 151 if (cmdp->argc != 0) 152 for (p = cmdp->argv[0]->bp; *p != '\0' && ISBLANK((UCHAR_T)*p); ++p); 153 154 /* If "write !" it's a pipe to a utility. */ 155 if (cmdp->argc != 0 && cmd == WRITE && *p == '!') { 156 /* Secure means no shell access. */ 157 if (O_ISSET(sp, O_SECURE)) { 158 ex_wemsg(sp, cmdp->cmd->name, EXM_SECURE_F); 159 return (1); 160 } 161 162 /* Expand the argument. */ 163 for (++p; *p && ISBLANK((UCHAR_T)*p); ++p); 164 if (*p == '\0') { 165 ex_emsg(sp, cmdp->cmd->usage, EXM_USAGE); 166 return (1); 167 } 168 if (argv_exp1(sp, cmdp, p, STRLEN(p), 1)) 169 return (1); 170 171 /* Set the last bang command */ 172 exp = EXP(sp); 173 free(exp->lastbcomm); 174 exp->lastbcomm = v_wstrdup(sp, cmdp->argv[1]->bp, 175 cmdp->argv[1]->len); 176 177 /* 178 * Historically, vi waited after a write filter even if there 179 * wasn't any output from the command. People complained when 180 * nvi waited only if there was output, wanting the visual cue 181 * that the program hadn't written anything. 182 */ 183 F_SET(sp, SC_EX_WAIT_YES); 184 185 /* 186 * !!! 187 * Ignore the return cursor position, the cursor doesn't 188 * move. 189 */ 190 if (ex_filter(sp, cmdp, &cmdp->addr1, 191 &cmdp->addr2, &rm, cmdp->argv[1]->bp, FILTER_WRITE)) 192 return (1); 193 194 /* Ex terminates with a bang, even if the command fails. */ 195 if (!F_ISSET(sp, SC_VI) && !F_ISSET(sp, SC_EX_SILENT)) 196 (void)ex_puts(sp, "!\n"); 197 198 return (0); 199 } 200 201 /* Set the FS_ALL flag if we're writing the entire file. */ 202 if (cmdp->addr1.lno <= 1 && !db_exist(sp, cmdp->addr2.lno + 1)) 203 LF_SET(FS_ALL); 204 205 /* If "write >>" it's an append to a file. */ 206 if (cmdp->argc != 0 && cmd != XIT && p[0] == '>' && p[1] == '>') { 207 LF_SET(FS_APPEND); 208 209 /* Skip ">>" and whitespace. */ 210 for (p += 2; *p && ISBLANK((UCHAR_T)*p); ++p); 211 } 212 213 /* If no other arguments, just write the file back. */ 214 if (cmdp->argc == 0 || *p == '\0') 215 return (file_write(sp, 216 &cmdp->addr1, &cmdp->addr2, NULL, flags)); 217 218 /* Build an argv so we get an argument count and file expansion. */ 219 if (argv_exp2(sp, cmdp, p, STRLEN(p))) 220 return (1); 221 222 /* 223 * 0 args: impossible. 224 * 1 args: impossible (I hope). 225 * 2 args: read it. 226 * >2 args: object, too many args. 227 * 228 * The 1 args case depends on the argv_sexp() function refusing 229 * to return success without at least one non-blank character. 230 */ 231 switch (cmdp->argc) { 232 case 0: 233 case 1: 234 abort(); 235 /* NOTREACHED */ 236 case 2: 237 INT2CHAR(sp, cmdp->argv[1]->bp, cmdp->argv[1]->len+1, 238 n, nlen); 239 name = v_strdup(sp, n, nlen - 1); 240 241 /* 242 * !!! 243 * Historically, the read and write commands renamed 244 * "unnamed" files, or, if the file had a name, set 245 * the alternate file name. 246 */ 247 if (F_ISSET(sp->frp, FR_TMPFILE) && 248 !F_ISSET(sp->frp, FR_EXNAMED)) { 249 char *q; 250 if ((q = v_strdup(sp, name, nlen - 1)) != NULL) { 251 free(sp->frp->name); 252 sp->frp->name = q; 253 } 254 /* 255 * The file has a real name, it's no longer a 256 * temporary, clear the temporary file flags. 257 * 258 * !!! 259 * If we're writing the whole file, FR_NAMECHANGE 260 * will be cleared by the write routine -- this is 261 * historic practice. 262 */ 263 F_CLR(sp->frp, FR_TMPEXIT | FR_TMPFILE); 264 F_SET(sp->frp, FR_NAMECHANGE | FR_EXNAMED); 265 266 /* Notify the screen. */ 267 (void)sp->gp->scr_rename(sp, sp->frp->name, 1); 268 } else 269 set_alt_name(sp, name); 270 break; 271 default: 272 INT2CHAR(sp, p, STRLEN(p) + 1, n, nlen); 273 ex_emsg(sp, n, EXM_FILECOUNT); 274 return (1); 275 } 276 277 rc = file_write(sp, &cmdp->addr1, &cmdp->addr2, name, flags); 278 279 free(name); 280 281 return rc; 282 } 283 284 /* 285 * ex_writefp -- 286 * Write a range of lines to a FILE *. 287 * 288 * PUBLIC: int ex_writefp __P((SCR *, 289 * PUBLIC: const char *, FILE *, MARK *, MARK *, u_long *, u_long *, int)); 290 */ 291 int 292 ex_writefp(SCR *sp, const char *name, FILE *fp, MARK *fm, MARK *tm, u_long *nlno, u_long *nch, int silent) 293 { 294 struct stat sb; 295 GS *gp; 296 u_long ccnt; /* XXX: can't print off_t portably. */ 297 db_recno_t fline, tline, lcnt; 298 size_t len; 299 int rval; 300 const char *msg; 301 CHAR_T *p; 302 const char *f; 303 size_t flen; 304 305 gp = sp->gp; 306 fline = fm->lno; 307 tline = tm->lno; 308 309 if (nlno != NULL) { 310 *nch = 0; 311 *nlno = 0; 312 } 313 314 /* 315 * The vi filter code has multiple processes running simultaneously, 316 * and one of them calls ex_writefp(). The "unsafe" function calls 317 * in this code are to db_get() and msgq(). Db_get() is safe, see 318 * the comment in ex_filter.c:ex_filter() for details. We don't call 319 * msgq if the multiple process bit in the EXF is set. 320 * 321 * !!! 322 * Historic vi permitted files of 0 length to be written. However, 323 * since the way vi got around dealing with "empty" files was to 324 * always have a line in the file no matter what, it wrote them as 325 * files of a single, empty line. We write empty files. 326 * 327 * "Alex, I'll take vi trivia for $1000." 328 */ 329 ccnt = 0; 330 lcnt = 0; 331 msg = "253|Writing..."; 332 if (tline != 0) 333 for (; fline <= tline; ++fline, ++lcnt) { 334 /* Caller has to provide any interrupt message. */ 335 if ((lcnt + 1) % INTERRUPT_CHECK == 0) { 336 if (INTERRUPTED(sp)) 337 break; 338 if (!silent) { 339 gp->scr_busy(sp, msg, msg == NULL ? 340 BUSY_UPDATE : BUSY_ON); 341 msg = NULL; 342 } 343 } 344 if (db_get(sp, fline, DBG_FATAL, &p, &len)) 345 goto err; 346 INT2FILE(sp, p, len, f, flen); 347 if (fwrite(f, 1, flen, fp) != flen) 348 goto err; 349 ccnt += len; 350 if (putc('\n', fp) != '\n') 351 break; 352 ++ccnt; 353 } 354 355 if (fflush(fp)) 356 goto err; 357 /* 358 * XXX 359 * I don't trust NFS -- check to make sure that we're talking to 360 * a regular file and sync so that NFS is forced to flush. 361 */ 362 if (!fstat(fileno(fp), &sb) && 363 S_ISREG(sb.st_mode) && fsync(fileno(fp))) 364 goto err; 365 366 if (fclose(fp)) 367 goto err; 368 369 rval = 0; 370 if (0) { 371 err: if (!F_ISSET(sp->ep, F_MULTILOCK)) 372 msgq_str(sp, M_SYSERR, name, "%s"); 373 (void)fclose(fp); 374 rval = 1; 375 } 376 377 if (!silent) 378 gp->scr_busy(sp, NULL, BUSY_OFF); 379 380 /* Report the possibly partial transfer. */ 381 if (nlno != NULL) { 382 *nch = ccnt; 383 *nlno = lcnt; 384 } 385 return (rval); 386 } 387