ex_write.c revision 285830
169450Smsmith/*- 269450Smsmith * Copyright (c) 1992, 1993, 1994 369450Smsmith * The Regents of the University of California. All rights reserved. 470243Smsmith * Copyright (c) 1992, 1993, 1994, 1995, 1996 569450Smsmith * Keith Bostic. All rights reserved. 669450Smsmith * 769450Smsmith * See the LICENSE file for redistribution information. 869450Smsmith */ 969450Smsmith 1069450Smsmith#include "config.h" 1169450Smsmith 1270243Smsmith#ifndef lint 1370243Smsmithstatic const char sccsid[] = "$Id: ex_write.c,v 10.41 2011/12/02 01:07:06 zy Exp $"; 1469450Smsmith#endif /* not lint */ 1569450Smsmith 1669450Smsmith#include <sys/types.h> 1769450Smsmith#include <sys/queue.h> 1869450Smsmith#include <sys/stat.h> 1969450Smsmith 2069450Smsmith#include <bitstring.h> 2169450Smsmith#include <ctype.h> 2269450Smsmith#include <errno.h> 2369450Smsmith#include <fcntl.h> 2469450Smsmith#include <limits.h> 2569450Smsmith#include <stdio.h> 2669450Smsmith#include <stdlib.h> 2769450Smsmith#include <string.h> 2869450Smsmith#include <strings.h> 2969450Smsmith#include <unistd.h> 3069450Smsmith 3169450Smsmith#include "../common/common.h" 3269450Smsmith 3369450Smsmithenum which {WN, WQ, WRITE, XIT}; 3469450Smsmithstatic int exwr __P((SCR *, EXCMD *, enum which)); 3569450Smsmith 3669450Smsmith/* 3769450Smsmith * ex_wn -- :wn[!] [>>] [file] 3869450Smsmith * Write to a file and switch to the next one. 3969450Smsmith * 4069450Smsmith * PUBLIC: int ex_wn __P((SCR *, EXCMD *)); 4169450Smsmith */ 4269450Smsmithint 4369450Smsmithex_wn(SCR *sp, EXCMD *cmdp) 4469450Smsmith{ 4569450Smsmith if (exwr(sp, cmdp, WN)) 4669450Smsmith return (1); 4769450Smsmith if (file_m3(sp, 0)) 4869450Smsmith return (1); 4969450Smsmith 5069450Smsmith /* The file name isn't a new file to edit. */ 5169450Smsmith cmdp->argc = 0; 5269450Smsmith 5369450Smsmith return (ex_next(sp, cmdp)); 5469450Smsmith} 5569450Smsmith 5669450Smsmith/* 5769450Smsmith * ex_wq -- :wq[!] [>>] [file] 5869450Smsmith * Write to a file and quit. 5969450Smsmith * 6069450Smsmith * PUBLIC: int ex_wq __P((SCR *, EXCMD *)); 6169450Smsmith */ 6269450Smsmithint 6369450Smsmithex_wq(SCR *sp, EXCMD *cmdp) 6469450Smsmith{ 6569450Smsmith int force; 6669450Smsmith 6769450Smsmith if (exwr(sp, cmdp, WQ)) 6869450Smsmith return (1); 6969450Smsmith if (file_m3(sp, 0)) 7069450Smsmith return (1); 7169450Smsmith 7269450Smsmith force = FL_ISSET(cmdp->iflags, E_C_FORCE); 7369450Smsmith 7469450Smsmith if (ex_ncheck(sp, force)) 7569450Smsmith return (1); 7669450Smsmith 7769450Smsmith F_SET(sp, force ? SC_EXIT_FORCE : SC_EXIT); 7869450Smsmith return (0); 7969450Smsmith} 8069450Smsmith 8169450Smsmith/* 8269450Smsmith * ex_write -- :write[!] [>>] [file] 8369450Smsmith * :write [!] [cmd] 8469450Smsmith * Write to a file. 8569450Smsmith * 8669450Smsmith * PUBLIC: int ex_write __P((SCR *, EXCMD *)); 8769450Smsmith */ 8869450Smsmithint 8969450Smsmithex_write(SCR *sp, EXCMD *cmdp) 9069450Smsmith{ 9169450Smsmith return (exwr(sp, cmdp, WRITE)); 9269450Smsmith} 9369450Smsmith 9469450Smsmith 9569450Smsmith/* 9669450Smsmith * ex_xit -- :x[it]! [file] 9769450Smsmith * Write out any modifications and quit. 9869450Smsmith * 9969450Smsmith * PUBLIC: int ex_xit __P((SCR *, EXCMD *)); 10069450Smsmith */ 10169450Smsmithint 10269450Smsmithex_xit(SCR *sp, EXCMD *cmdp) 10369450Smsmith{ 10469450Smsmith int force; 10569450Smsmith 10669450Smsmith NEEDFILE(sp, cmdp); 10769450Smsmith 10869450Smsmith if (F_ISSET(sp->ep, F_MODIFIED) && exwr(sp, cmdp, XIT)) 10969450Smsmith return (1); 11069450Smsmith if (file_m3(sp, 0)) 11169450Smsmith return (1); 11269450Smsmith 11369450Smsmith force = FL_ISSET(cmdp->iflags, E_C_FORCE); 11469450Smsmith 11569450Smsmith if (ex_ncheck(sp, force)) 11669450Smsmith return (1); 11769450Smsmith 11869450Smsmith F_SET(sp, force ? SC_EXIT_FORCE : SC_EXIT); 11969450Smsmith return (0); 12069450Smsmith} 12169450Smsmith 12269450Smsmith/* 12369450Smsmith * exwr -- 12469450Smsmith * The guts of the ex write commands. 12569450Smsmith */ 12669450Smsmithstatic int 12769450Smsmithexwr(SCR *sp, EXCMD *cmdp, enum which cmd) 12869450Smsmith{ 12969450Smsmith MARK rm; 13069450Smsmith int flags; 13169450Smsmith char *name; 13269450Smsmith CHAR_T *p = NULL; 13369450Smsmith size_t nlen; 13469450Smsmith char *n; 13569450Smsmith int rc; 13669450Smsmith EX_PRIVATE *exp; 13769450Smsmith 13869450Smsmith NEEDFILE(sp, cmdp); 13969450Smsmith 14069450Smsmith /* All write commands can have an associated '!'. */ 14169450Smsmith LF_INIT(FS_POSSIBLE); 14269450Smsmith if (FL_ISSET(cmdp->iflags, E_C_FORCE)) 14369450Smsmith LF_SET(FS_FORCE); 14469450Smsmith 14569450Smsmith /* Skip any leading whitespace. */ 14669450Smsmith if (cmdp->argc != 0) 14769450Smsmith for (p = cmdp->argv[0]->bp; *p != '\0' && cmdskip(*p); ++p); 14869450Smsmith 14969450Smsmith /* If "write !" it's a pipe to a utility. */ 15069450Smsmith if (cmdp->argc != 0 && cmd == WRITE && *p == '!') { 15169450Smsmith /* Secure means no shell access. */ 15269450Smsmith if (O_ISSET(sp, O_SECURE)) { 15369450Smsmith ex_wemsg(sp, cmdp->cmd->name, EXM_SECURE_F); 15469450Smsmith return (1); 15569450Smsmith } 15669450Smsmith 15769450Smsmith /* Expand the argument. */ 15869450Smsmith for (++p; *p && cmdskip(*p); ++p); 15969450Smsmith if (*p == '\0') { 16069450Smsmith ex_emsg(sp, cmdp->cmd->usage, EXM_USAGE); 16169450Smsmith return (1); 16269450Smsmith } 16369450Smsmith if (argv_exp1(sp, cmdp, p, STRLEN(p), 1)) 16469450Smsmith return (1); 16569450Smsmith 16669450Smsmith /* Set the last bang command */ 16769450Smsmith exp = EXP(sp); 16869450Smsmith free(exp->lastbcomm); 16969450Smsmith exp->lastbcomm = v_wstrdup(sp, cmdp->argv[1]->bp, 17069450Smsmith cmdp->argv[1]->len); 17169450Smsmith 17269450Smsmith /* 17369450Smsmith * Historically, vi waited after a write filter even if there 17469450Smsmith * wasn't any output from the command. People complained when 17569450Smsmith * nvi waited only if there was output, wanting the visual cue 17669450Smsmith * that the program hadn't written anything. 17769450Smsmith */ 17869450Smsmith F_SET(sp, SC_EX_WAIT_YES); 17969450Smsmith 18069450Smsmith /* 18169450Smsmith * !!! 18269450Smsmith * Ignore the return cursor position, the cursor doesn't 18369450Smsmith * move. 18469450Smsmith */ 18569450Smsmith if (ex_filter(sp, cmdp, &cmdp->addr1, 18669450Smsmith &cmdp->addr2, &rm, cmdp->argv[1]->bp, FILTER_WRITE)) 18769450Smsmith return (1); 18869450Smsmith 18969450Smsmith /* Ex terminates with a bang, even if the command fails. */ 19069450Smsmith if (!F_ISSET(sp, SC_VI) && !F_ISSET(sp, SC_EX_SILENT)) 19169450Smsmith (void)ex_puts(sp, "!\n"); 19269450Smsmith 19369450Smsmith return (0); 19469450Smsmith } 19569450Smsmith 19669450Smsmith /* Set the FS_ALL flag if we're writing the entire file. */ 19769450Smsmith if (cmdp->addr1.lno <= 1 && !db_exist(sp, cmdp->addr2.lno + 1)) 19869450Smsmith LF_SET(FS_ALL); 19969450Smsmith 20069450Smsmith /* If "write >>" it's an append to a file. */ 20169450Smsmith if (cmdp->argc != 0 && cmd != XIT && p[0] == '>' && p[1] == '>') { 20269450Smsmith LF_SET(FS_APPEND); 20369450Smsmith 20469450Smsmith /* Skip ">>" and whitespace. */ 20569450Smsmith for (p += 2; *p && cmdskip(*p); ++p); 20669450Smsmith } 20769450Smsmith 20869450Smsmith /* If no other arguments, just write the file back. */ 20969450Smsmith if (cmdp->argc == 0 || *p == '\0') 21069450Smsmith return (file_write(sp, 21169450Smsmith &cmdp->addr1, &cmdp->addr2, NULL, flags)); 21269450Smsmith 21369450Smsmith /* Build an argv so we get an argument count and file expansion. */ 21469450Smsmith if (argv_exp2(sp, cmdp, p, STRLEN(p))) 215 return (1); 216 217 /* 218 * 0 args: impossible. 219 * 1 args: impossible (I hope). 220 * 2 args: read it. 221 * >2 args: object, too many args. 222 * 223 * The 1 args case depends on the argv_sexp() function refusing 224 * to return success without at least one non-blank character. 225 */ 226 switch (cmdp->argc) { 227 case 0: 228 case 1: 229 abort(); 230 /* NOTREACHED */ 231 case 2: 232 INT2CHAR(sp, cmdp->argv[1]->bp, cmdp->argv[1]->len+1, 233 n, nlen); 234 name = v_strdup(sp, n, nlen - 1); 235 236 /* 237 * !!! 238 * Historically, the read and write commands renamed 239 * "unnamed" files, or, if the file had a name, set 240 * the alternate file name. 241 */ 242 if (F_ISSET(sp->frp, FR_TMPFILE) && 243 !F_ISSET(sp->frp, FR_EXNAMED)) { 244 if ((n = v_strdup(sp, name, nlen - 1)) != NULL) { 245 free(sp->frp->name); 246 sp->frp->name = n; 247 } 248 /* 249 * The file has a real name, it's no longer a 250 * temporary, clear the temporary file flags. 251 * 252 * !!! 253 * If we're writing the whole file, FR_NAMECHANGE 254 * will be cleared by the write routine -- this is 255 * historic practice. 256 */ 257 F_CLR(sp->frp, FR_TMPEXIT | FR_TMPFILE); 258 F_SET(sp->frp, FR_NAMECHANGE | FR_EXNAMED); 259 260 /* Notify the screen. */ 261 (void)sp->gp->scr_rename(sp, sp->frp->name, 1); 262 } else 263 set_alt_name(sp, name); 264 break; 265 default: 266 INT2CHAR(sp, p, STRLEN(p) + 1, n, nlen); 267 ex_emsg(sp, n, EXM_FILECOUNT); 268 return (1); 269 } 270 271 rc = file_write(sp, &cmdp->addr1, &cmdp->addr2, name, flags); 272 273 free(name); 274 275 return rc; 276} 277 278/* 279 * ex_writefp -- 280 * Write a range of lines to a FILE *. 281 * 282 * PUBLIC: int ex_writefp __P((SCR *, 283 * PUBLIC: char *, FILE *, MARK *, MARK *, u_long *, u_long *, int)); 284 */ 285int 286ex_writefp(SCR *sp, char *name, FILE *fp, MARK *fm, MARK *tm, u_long *nlno, u_long *nch, int silent) 287{ 288 struct stat sb; 289 GS *gp; 290 u_long ccnt; /* XXX: can't print off_t portably. */ 291 recno_t fline, tline, lcnt; 292 size_t len; 293 int rval; 294 char *msg; 295 CHAR_T *p; 296 char *f; 297 size_t flen; 298 int isutf16; 299 300 gp = sp->gp; 301 fline = fm->lno; 302 tline = tm->lno; 303 304 if (nlno != NULL) { 305 *nch = 0; 306 *nlno = 0; 307 } 308 309 /* 310 * The vi filter code has multiple processes running simultaneously, 311 * and one of them calls ex_writefp(). The "unsafe" function calls 312 * in this code are to db_get() and msgq(). Db_get() is safe, see 313 * the comment in ex_filter.c:ex_filter() for details. We don't call 314 * msgq if the multiple process bit in the EXF is set. 315 * 316 * !!! 317 * Historic vi permitted files of 0 length to be written. However, 318 * since the way vi got around dealing with "empty" files was to 319 * always have a line in the file no matter what, it wrote them as 320 * files of a single, empty line. We write empty files. 321 * 322 * "Alex, I'll take vi trivia for $1000." 323 */ 324 ccnt = 0; 325 lcnt = 0; 326 msg = "253|Writing..."; 327 328 if (O_ISSET(sp, O_FILEENCODING)) { 329 isutf16 = !strncasecmp(O_STR(sp, O_FILEENCODING), "utf-16", 6); 330 isutf16 += !strncasecmp(O_STR(sp, O_FILEENCODING), "utf-16le", 8); 331 } else isutf16 = 0; 332 333 if (tline != 0) { 334 if (isutf16 == 1 && fwrite("\xfe\xff", 1, 2, fp) != 2) 335 goto err; 336 if (isutf16 == 2 && fwrite("\xff\xfe", 1, 2, fp) != 2) 337 goto err; 338 for (; fline <= tline; ++fline, ++lcnt) { 339 /* Caller has to provide any interrupt message. */ 340 if ((lcnt + 1) % INTERRUPT_CHECK == 0) { 341 if (INTERRUPTED(sp)) 342 break; 343 if (!silent) { 344 gp->scr_busy(sp, msg, msg == NULL ? 345 BUSY_UPDATE : BUSY_ON); 346 msg = NULL; 347 } 348 } 349 if (db_get(sp, fline, DBG_FATAL, &p, &len)) 350 goto err; 351 INT2FILE(sp, p, len, f, flen); 352 if (fwrite(f, 1, flen, fp) != flen) 353 goto err; 354 ccnt += len; 355 /* UTF-16 w/o BOM is big-endian */ 356 switch (isutf16) { 357 case 1: /* UTF-16BE */ 358 if (fwrite("\0\x0a", 1, 2, fp) != 2) 359 goto done; 360 break; 361 case 2: /* UTF-16LE */ 362 if (fwrite("\x0a\0", 1, 2, fp) != 2) 363 goto done; 364 break; 365 default: 366 if (putc('\n', fp) != '\n') 367 goto done; 368 } 369 ++ccnt; 370 } 371 } 372 373done: if (fflush(fp)) 374 goto err; 375 /* 376 * XXX 377 * I don't trust NFS -- check to make sure that we're talking to 378 * a regular file and sync so that NFS is forced to flush. 379 */ 380 if (!fstat(fileno(fp), &sb) && 381 S_ISREG(sb.st_mode) && fsync(fileno(fp))) 382 goto err; 383 384 if (fclose(fp)) 385 goto err; 386 387 rval = 0; 388 if (0) { 389err: if (!F_ISSET(sp->ep, F_MULTILOCK)) 390 msgq_str(sp, M_SYSERR, name, "%s"); 391 (void)fclose(fp); 392 rval = 1; 393 } 394 395 if (!silent) 396 gp->scr_busy(sp, NULL, BUSY_OFF); 397 398 /* Report the possibly partial transfer. */ 399 if (nlno != NULL) { 400 *nch = ccnt; 401 *nlno = lcnt; 402 } 403 return (rval); 404} 405