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