This commit was generated by cvs2svn to track changes on a CVS vendor
[unix-history] / usr.bin / vi / nex / ex_write.c
CommitLineData
18ae9d6b
AS
1/*-
2 * Copyright (c) 1992, 1993
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
35static char sccsid[] = "@(#)ex_write.c 8.19 (Berkeley) 12/18/93";
36#endif /* not lint */
37
38#include <sys/types.h>
39#include <sys/stat.h>
40
41#include <ctype.h>
42#include <errno.h>
43#include <fcntl.h>
44#include <string.h>
45#include <unistd.h>
46
47#include "vi.h"
48#include "excmd.h"
49
50enum which {WQ, WRITE, XIT};
51
52static int exwr __P((SCR *, EXF *, EXCMDARG *, enum which));
53
54/*
55 * ex_wq -- :wq[!] [>>] [file]
56 * Write to a file.
57 */
58int
59ex_wq(sp, ep, cmdp)
60 SCR *sp;
61 EXF *ep;
62 EXCMDARG *cmdp;
63{
64 int force;
65
66 if (exwr(sp, ep, cmdp, WQ))
67 return (1);
68
69 force = F_ISSET(cmdp, E_FORCE);
70 if (!force && ep->refcnt <= 1 && file_unedited(sp) != NULL) {
71 msgq(sp, M_ERR,
72 "More files to edit; use \":n\" to go to the next file");
73 return (1);
74 }
75
76 F_SET(sp, force ? S_EXIT_FORCE : S_EXIT);
77 return (0);
78}
79
80/*
81 * ex_write -- :write[!] [>>] [file]
82 * :write [!] [cmd]
83 * Write to a file.
84 */
85int
86ex_write(sp, ep, cmdp)
87 SCR *sp;
88 EXF *ep;
89 EXCMDARG *cmdp;
90{
91 return (exwr(sp, ep, cmdp, WRITE));
92}
93
94
95/*
96 * ex_xit -- :x[it]! [file]
97 *
98 * Write out any modifications and quit.
99 */
100int
101ex_xit(sp, ep, cmdp)
102 SCR *sp;
103 EXF *ep;
104 EXCMDARG *cmdp;
105{
106 int force;
107
108 if (F_ISSET((ep), F_MODIFIED) && exwr(sp, ep, cmdp, XIT))
109 return (1);
110
111 force = F_ISSET(cmdp, E_FORCE);
112 if (!force && ep->refcnt <= 1 && file_unedited(sp) != NULL) {
113 msgq(sp, M_ERR,
114 "More files to edit; use \":n\" to go to the next file");
115 return (1);
116 }
117
118 F_SET(sp, force ? S_EXIT_FORCE : S_EXIT);
119 return (0);
120}
121
122/*
123 * exwr --
124 * The guts of the ex write commands.
125 */
126static int
127exwr(sp, ep, cmdp, cmd)
128 SCR *sp;
129 EXF *ep;
130 EXCMDARG *cmdp;
131 enum which cmd;
132{
133 EX_PRIVATE *exp;
134 MARK rm;
135 int flags;
136 char *name, *p;
137
138 /* All write commands can have an associated '!'. */
139 LF_INIT(FS_POSSIBLE);
140 if (F_ISSET(cmdp, E_FORCE))
141 LF_SET(FS_FORCE);
142
143 /* Skip any leading whitespace. */
144 if (cmdp->argc != 0)
145 for (p = cmdp->argv[0]->bp; *p && isblank(*p); ++p);
146
147 /* If no arguments, just write the file back. */
148 if (cmdp->argc == 0 || *p == '\0') {
149 if (F_ISSET(cmdp, E_ADDR2_ALL))
150 LF_SET(FS_ALL);
151 return (file_write(sp, ep,
152 &cmdp->addr1, &cmdp->addr2, NULL, flags));
153 }
154
155 /* If "write !" it's a pipe to a utility. */
156 exp = EXP(sp);
157 if (cmd == WRITE && *p == '!') {
158 for (++p; *p && isblank(*p); ++p);
159 if (*p == '\0') {
160 msgq(sp, M_ERR, "Usage: %s.", cmdp->cmd->usage);
161 return (1);
162 }
163 /* Expand the argument. */
164 if (argv_exp1(sp, ep, cmdp, p, strlen(p), 0))
165 return (1);
166 if (filtercmd(sp, ep, &cmdp->addr1, &cmdp->addr2,
167 &rm, cmdp->argv[1]->bp, FILTER_WRITE))
168 return (1);
169 sp->lno = rm.lno;
170 return (0);
171 }
172
173 /* If "write >>" it's an append to a file. */
174 if (cmd != XIT && p[0] == '>' && p[1] == '>') {
175 LF_SET(FS_APPEND);
176
177 /* Skip ">>" and whitespace. */
178 for (p += 2; *p && isblank(*p); ++p);
179 }
180
181 /* Build an argv so we get an argument count and file expansion. */
182 if (argv_exp2(sp, ep, cmdp, p, strlen(p), 0))
183 return (1);
184
185 switch (cmdp->argc) {
186 case 1:
187 /*
188 * Nothing to expand, write the current file.
189 * XXX
190 * Should never happen, already checked this case.
191 */
192 name = NULL;
193 break;
194 case 2:
195 /* One new argument, write it. */
196 name = cmdp->argv[exp->argsoff - 1]->bp;
197 set_alt_name(sp, name);
198 break;
199 default:
200 /* If expanded to more than one argument, object. */
201 msgq(sp, M_ERR, "%s expanded into too many file names",
202 cmdp->argv[0]->bp);
203 msgq(sp, M_ERR, "Usage: %s.", cmdp->cmd->usage);
204 return (1);
205 }
206
207 if (F_ISSET(cmdp, E_ADDR2_ALL))
208 LF_SET(FS_ALL);
209 return (file_write(sp, ep, &cmdp->addr1, &cmdp->addr2, name, flags));
210}
211
212/*
213 * ex_writefp --
214 * Write a range of lines to a FILE *.
215 */
216int
217ex_writefp(sp, ep, name, fp, fm, tm, nlno, nch)
218 SCR *sp;
219 EXF *ep;
220 char *name;
221 FILE *fp;
222 MARK *fm, *tm;
223 u_long *nlno, *nch;
224{
225 register u_long ccnt, fline, tline;
226 size_t len;
227 char *p;
228
229 fline = fm->lno;
230 tline = tm->lno;
231
232 if (nlno != NULL) {
233 *nch = 0;
234 *nlno = 0;
235 }
236 ccnt = 0;
237
238 /*
239 * The vi filter code has multiple processes running simultaneously,
240 * and one of them calls ex_writefp(). The "unsafe" function calls
241 * in this code are to file_gline() and msgq(). File_gline() is safe,
242 * see the comment in filter.c:filtercmd() for details. We don't call
243 * msgq if the multiple process bit in the EXF is set.
244 *
245 * !!!
246 * Historic vi permitted files of 0 length to be written. However,
247 * since the way vi got around dealing with "empty" files was to
248 * always have a line in the file no matter what, it wrote them as
249 * files of a single, empty line. We write empty files.
250 *
251 * "Alex, I'll take vi trivia for $1000."
252 */
253 if (tline != 0)
254 for (; fline <= tline; ++fline) {
255 if ((p = file_gline(sp, ep, fline, &len)) == NULL)
256 break;
257 if (fwrite(p, 1, len, fp) != len) {
258 msgq(sp, M_SYSERR, name);
259 (void)fclose(fp);
260 return (1);
261 }
262 ccnt += len;
263 if (putc('\n', fp) != '\n')
264 break;
265 ++ccnt;
266 }
267 if (fclose(fp)) {
268 if (!F_ISSET(ep, F_MULTILOCK))
269 msgq(sp, M_SYSERR, name);
270 return (1);
271 }
272 if (nlno != NULL) {
273 *nch = ccnt;
274 *nlno = tm->lno == 0 ? 0 : tm->lno - fm->lno + 1;
275 }
276 return (0);
277}