GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/vi/build/../ex/ex_write.c Lines: 0 112 0.0 %
Date: 2017-11-07 Branches: 0 124 0.0 %

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