1 | /*- |
---|
2 | * Copyright (c) 1992, 1993, 1994 |
---|
3 | * The Regents of the University of California. All rights reserved. |
---|
4 | * Copyright (c) 1992, 1993, 1994, 1995, 1996 |
---|
5 | * Keith Bostic. All rights reserved. |
---|
6 | * |
---|
7 | * See the LICENSE file for redistribution information. |
---|
8 | */ |
---|
9 | |
---|
10 | #include "config.h" |
---|
11 | |
---|
12 | #ifndef lint |
---|
13 | static const char sccsid[] = "@(#)exf.c 10.49 (Berkeley) 10/10/96"; |
---|
14 | #endif /* not lint */ |
---|
15 | |
---|
16 | #include <sys/param.h> |
---|
17 | #include <sys/types.h> /* XXX: param.h may not have included types.h */ |
---|
18 | #include <sys/queue.h> |
---|
19 | #include <sys/stat.h> |
---|
20 | |
---|
21 | /* |
---|
22 | * We include <sys/file.h>, because the flock(2) and open(2) #defines |
---|
23 | * were found there on historical systems. We also include <fcntl.h> |
---|
24 | * because the open(2) #defines are found there on newer systems. |
---|
25 | */ |
---|
26 | #include <sys/file.h> |
---|
27 | |
---|
28 | #include <bitstring.h> |
---|
29 | #include <dirent.h> |
---|
30 | #include <errno.h> |
---|
31 | #include <fcntl.h> |
---|
32 | #include <limits.h> |
---|
33 | #include <stdio.h> |
---|
34 | #include <stdlib.h> |
---|
35 | #include <string.h> |
---|
36 | #include <unistd.h> |
---|
37 | |
---|
38 | #include "common.h" |
---|
39 | |
---|
40 | static int file_backup __P((SCR *, char *, char *)); |
---|
41 | static void file_cinit __P((SCR *)); |
---|
42 | static void file_comment __P((SCR *)); |
---|
43 | static int file_spath __P((SCR *, FREF *, struct stat *, int *)); |
---|
44 | |
---|
45 | /* |
---|
46 | * file_add -- |
---|
47 | * Insert a file name into the FREF list, if it doesn't already |
---|
48 | * appear in it. |
---|
49 | * |
---|
50 | * !!! |
---|
51 | * The "if it doesn't already appear" changes vi's semantics slightly. If |
---|
52 | * you do a "vi foo bar", and then execute "next bar baz", the edit of bar |
---|
53 | * will reflect the line/column of the previous edit session. Historic nvi |
---|
54 | * did not do this. The change is a logical extension of the change where |
---|
55 | * vi now remembers the last location in any file that it has ever edited, |
---|
56 | * not just the previously edited file. |
---|
57 | * |
---|
58 | * PUBLIC: FREF *file_add __P((SCR *, CHAR_T *)); |
---|
59 | */ |
---|
60 | FREF * |
---|
61 | file_add(sp, name) |
---|
62 | SCR *sp; |
---|
63 | CHAR_T *name; |
---|
64 | { |
---|
65 | GS *gp; |
---|
66 | FREF *frp, *tfrp; |
---|
67 | |
---|
68 | /* |
---|
69 | * Return it if it already exists. Note that we test against the |
---|
70 | * user's name, whatever that happens to be, including if it's a |
---|
71 | * temporary file. |
---|
72 | * |
---|
73 | * If the user added a file but was unable to initialize it, there |
---|
74 | * can be file list entries where the name field is NULL. Discard |
---|
75 | * them the next time we see them. |
---|
76 | */ |
---|
77 | gp = sp->gp; |
---|
78 | if (name != NULL) |
---|
79 | for (frp = gp->frefq.cqh_first; |
---|
80 | frp != (FREF *)&gp->frefq; frp = frp->q.cqe_next) { |
---|
81 | if (frp->name == NULL) { |
---|
82 | tfrp = frp->q.cqe_next; |
---|
83 | CIRCLEQ_REMOVE(&gp->frefq, frp, q); |
---|
84 | if (frp->name != NULL) |
---|
85 | free(frp->name); |
---|
86 | free(frp); |
---|
87 | frp = tfrp; |
---|
88 | continue; |
---|
89 | } |
---|
90 | if (!strcmp(frp->name, name)) |
---|
91 | return (frp); |
---|
92 | } |
---|
93 | |
---|
94 | /* Allocate and initialize the FREF structure. */ |
---|
95 | CALLOC(sp, frp, FREF *, 1, sizeof(FREF)); |
---|
96 | if (frp == NULL) |
---|
97 | return (NULL); |
---|
98 | |
---|
99 | /* |
---|
100 | * If no file name specified, or if the file name is a request |
---|
101 | * for something temporary, file_init() will allocate the file |
---|
102 | * name. Temporary files are always ignored. |
---|
103 | */ |
---|
104 | if (name != NULL && strcmp(name, TEMPORARY_FILE_STRING) && |
---|
105 | (frp->name = strdup(name)) == NULL) { |
---|
106 | free(frp); |
---|
107 | msgq(sp, M_SYSERR, NULL); |
---|
108 | return (NULL); |
---|
109 | } |
---|
110 | |
---|
111 | /* Append into the chain of file names. */ |
---|
112 | CIRCLEQ_INSERT_TAIL(&gp->frefq, frp, q); |
---|
113 | |
---|
114 | return (frp); |
---|
115 | } |
---|
116 | |
---|
117 | /* |
---|
118 | * file_init -- |
---|
119 | * Start editing a file, based on the FREF structure. If successsful, |
---|
120 | * let go of any previous file. Don't release the previous file until |
---|
121 | * absolutely sure we have the new one. |
---|
122 | * |
---|
123 | * PUBLIC: int file_init __P((SCR *, FREF *, char *, int)); |
---|
124 | */ |
---|
125 | int |
---|
126 | file_init(sp, frp, rcv_name, flags) |
---|
127 | SCR *sp; |
---|
128 | FREF *frp; |
---|
129 | char *rcv_name; |
---|
130 | int flags; |
---|
131 | { |
---|
132 | EXF *ep; |
---|
133 | RECNOINFO oinfo; |
---|
134 | struct stat sb; |
---|
135 | size_t psize; |
---|
136 | int fd, exists, open_err, readonly; |
---|
137 | char *oname, tname[MAXPATHLEN]; |
---|
138 | |
---|
139 | open_err = readonly = 0; |
---|
140 | |
---|
141 | /* |
---|
142 | * If the file is a recovery file, let the recovery code handle it. |
---|
143 | * Clear the FR_RECOVER flag first -- the recovery code does set up, |
---|
144 | * and then calls us! If the recovery call fails, it's probably |
---|
145 | * because the named file doesn't exist. So, move boldly forward, |
---|
146 | * presuming that there's an error message the user will get to see. |
---|
147 | */ |
---|
148 | if (F_ISSET(frp, FR_RECOVER)) { |
---|
149 | F_CLR(frp, FR_RECOVER); |
---|
150 | return (rcv_read(sp, frp)); |
---|
151 | } |
---|
152 | |
---|
153 | /* |
---|
154 | * Required FRP initialization; the only flag we keep is the |
---|
155 | * cursor information. |
---|
156 | */ |
---|
157 | F_CLR(frp, ~FR_CURSORSET); |
---|
158 | |
---|
159 | /* |
---|
160 | * Required EXF initialization: |
---|
161 | * Flush the line caches. |
---|
162 | * Default recover mail file fd to -1. |
---|
163 | * Set initial EXF flag bits. |
---|
164 | */ |
---|
165 | CALLOC_RET(sp, ep, EXF *, 1, sizeof(EXF)); |
---|
166 | ep->c_lno = ep->c_nlines = OOBLNO; |
---|
167 | ep->rcv_fd = ep->fcntl_fd = -1; |
---|
168 | F_SET(ep, F_FIRSTMODIFY); |
---|
169 | |
---|
170 | /* |
---|
171 | * Scan the user's path to find the file that we're going to |
---|
172 | * try and open. |
---|
173 | */ |
---|
174 | if (file_spath(sp, frp, &sb, &exists)) |
---|
175 | return (1); |
---|
176 | |
---|
177 | /* |
---|
178 | * If no name or backing file, for whatever reason, create a backing |
---|
179 | * temporary file, saving the temp file name so we can later unlink |
---|
180 | * it. If the user never named this file, copy the temporary file name |
---|
181 | * to the real name (we display that until the user renames it). |
---|
182 | */ |
---|
183 | oname = frp->name; |
---|
184 | if (LF_ISSET(FS_OPENERR) || oname == NULL || !exists) { |
---|
185 | if (opts_empty(sp, O_DIRECTORY, 0)) |
---|
186 | goto err; |
---|
187 | (void)snprintf(tname, sizeof(tname), |
---|
188 | "%s/vi.XXXXXX", O_STR(sp, O_DIRECTORY)); |
---|
189 | if ((fd = mkstemp(tname)) == -1) { |
---|
190 | msgq(sp, M_SYSERR, |
---|
191 | "237|Unable to create temporary file"); |
---|
192 | goto err; |
---|
193 | } |
---|
194 | (void)close(fd); |
---|
195 | |
---|
196 | if (frp->name == NULL) |
---|
197 | F_SET(frp, FR_TMPFILE); |
---|
198 | if ((frp->tname = strdup(tname)) == NULL || |
---|
199 | frp->name == NULL && (frp->name = strdup(tname)) == NULL) { |
---|
200 | if (frp->tname != NULL) |
---|
201 | free(frp->tname); |
---|
202 | msgq(sp, M_SYSERR, NULL); |
---|
203 | (void)unlink(tname); |
---|
204 | goto err; |
---|
205 | } |
---|
206 | oname = frp->tname; |
---|
207 | psize = 1024; |
---|
208 | if (!LF_ISSET(FS_OPENERR)) |
---|
209 | F_SET(frp, FR_NEWFILE); |
---|
210 | |
---|
211 | time(&ep->mtime); |
---|
212 | } else { |
---|
213 | /* |
---|
214 | * XXX |
---|
215 | * A seat of the pants calculation: try to keep the file in |
---|
216 | * 15 pages or less. Don't use a page size larger than 10K |
---|
217 | * (vi should have good locality) or smaller than 1K. |
---|
218 | */ |
---|
219 | psize = ((sb.st_size / 15) + 1023) / 1024; |
---|
220 | if (psize > 10) |
---|
221 | psize = 10; |
---|
222 | if (psize == 0) |
---|
223 | psize = 1; |
---|
224 | psize *= 1024; |
---|
225 | |
---|
226 | F_SET(ep, F_DEVSET); |
---|
227 | ep->mdev = sb.st_dev; |
---|
228 | ep->minode = sb.st_ino; |
---|
229 | |
---|
230 | ep->mtime = sb.st_mtime; |
---|
231 | |
---|
232 | if (!S_ISREG(sb.st_mode)) |
---|
233 | msgq_str(sp, M_ERR, oname, |
---|
234 | "238|Warning: %s is not a regular file"); |
---|
235 | } |
---|
236 | |
---|
237 | /* Set up recovery. */ |
---|
238 | memset(&oinfo, 0, sizeof(RECNOINFO)); |
---|
239 | oinfo.bval = '\n'; /* Always set. */ |
---|
240 | oinfo.psize = psize; |
---|
241 | oinfo.flags = F_ISSET(sp->gp, G_SNAPSHOT) ? R_SNAPSHOT : 0; |
---|
242 | if (rcv_name == NULL) { |
---|
243 | if (!rcv_tmp(sp, ep, frp->name)) |
---|
244 | oinfo.bfname = ep->rcv_path; |
---|
245 | } else { |
---|
246 | if ((ep->rcv_path = strdup(rcv_name)) == NULL) { |
---|
247 | msgq(sp, M_SYSERR, NULL); |
---|
248 | goto err; |
---|
249 | } |
---|
250 | oinfo.bfname = ep->rcv_path; |
---|
251 | F_SET(ep, F_MODIFIED); |
---|
252 | } |
---|
253 | |
---|
254 | /* Open a db structure. */ |
---|
255 | if ((ep->db = dbopen(rcv_name == NULL ? oname : NULL, |
---|
256 | O_NONBLOCK | O_RDONLY, |
---|
257 | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, |
---|
258 | DB_RECNO, &oinfo)) == NULL) { |
---|
259 | msgq_str(sp, |
---|
260 | M_SYSERR, rcv_name == NULL ? oname : rcv_name, "%s"); |
---|
261 | /* |
---|
262 | * !!! |
---|
263 | * Historically, vi permitted users to edit files that couldn't |
---|
264 | * be read. This isn't useful for single files from a command |
---|
265 | * line, but it's quite useful for "vi *.c", since you can skip |
---|
266 | * past files that you can't read. |
---|
267 | */ |
---|
268 | open_err = 1; |
---|
269 | goto oerr; |
---|
270 | } |
---|
271 | |
---|
272 | /* |
---|
273 | * Do the remaining things that can cause failure of the new file, |
---|
274 | * mark and logging initialization. |
---|
275 | */ |
---|
276 | if (mark_init(sp, ep) || log_init(sp, ep)) |
---|
277 | goto err; |
---|
278 | |
---|
279 | /* |
---|
280 | * Set the alternate file name to be the file we're discarding. |
---|
281 | * |
---|
282 | * !!! |
---|
283 | * Temporary files can't become alternate files, so there's no file |
---|
284 | * name. This matches historical practice, although it could only |
---|
285 | * happen in historical vi as the result of the initial command, i.e. |
---|
286 | * if vi was executed without a file name. |
---|
287 | */ |
---|
288 | if (LF_ISSET(FS_SETALT)) |
---|
289 | set_alt_name(sp, sp->frp == NULL || |
---|
290 | F_ISSET(sp->frp, FR_TMPFILE) ? NULL : sp->frp->name); |
---|
291 | |
---|
292 | /* |
---|
293 | * Close the previous file; if that fails, close the new one and run |
---|
294 | * for the border. |
---|
295 | * |
---|
296 | * !!! |
---|
297 | * There's a nasty special case. If the user edits a temporary file, |
---|
298 | * and then does an ":e! %", we need to re-initialize the backing |
---|
299 | * file, but we can't change the name. (It's worse -- we're dealing |
---|
300 | * with *names* here, we can't even detect that it happened.) Set a |
---|
301 | * flag so that the file_end routine ignores the backing information |
---|
302 | * of the old file if it happens to be the same as the new one. |
---|
303 | * |
---|
304 | * !!! |
---|
305 | * Side-effect: after the call to file_end(), sp->frp may be NULL. |
---|
306 | */ |
---|
307 | if (sp->ep != NULL) { |
---|
308 | F_SET(frp, FR_DONTDELETE); |
---|
309 | if (file_end(sp, NULL, LF_ISSET(FS_FORCE))) { |
---|
310 | (void)file_end(sp, ep, 1); |
---|
311 | goto err; |
---|
312 | } |
---|
313 | F_CLR(frp, FR_DONTDELETE); |
---|
314 | } |
---|
315 | |
---|
316 | /* |
---|
317 | * Lock the file; if it's a recovery file, it should already be |
---|
318 | * locked. Note, we acquire the lock after the previous file |
---|
319 | * has been ended, so that we don't get an "already locked" error |
---|
320 | * for ":edit!". |
---|
321 | * |
---|
322 | * XXX |
---|
323 | * While the user can't interrupt us between the open and here, |
---|
324 | * there's a race between the dbopen() and the lock. Not much |
---|
325 | * we can do about it. |
---|
326 | * |
---|
327 | * XXX |
---|
328 | * We don't make a big deal of not being able to lock the file. As |
---|
329 | * locking rarely works over NFS, and often fails if the file was |
---|
330 | * mmap(2)'d, it's far too common to do anything like print an error |
---|
331 | * message, let alone make the file readonly. At some future time, |
---|
332 | * when locking is a little more reliable, this should change to be |
---|
333 | * an error. |
---|
334 | */ |
---|
335 | if (rcv_name == NULL) |
---|
336 | switch (file_lock(sp, oname, |
---|
337 | &ep->fcntl_fd, ep->db->fd(ep->db), 0)) { |
---|
338 | case LOCK_FAILED: |
---|
339 | F_SET(frp, FR_UNLOCKED); |
---|
340 | break; |
---|
341 | case LOCK_UNAVAIL: |
---|
342 | readonly = 1; |
---|
343 | msgq_str(sp, M_INFO, oname, |
---|
344 | "239|%s already locked, session is read-only"); |
---|
345 | break; |
---|
346 | case LOCK_SUCCESS: |
---|
347 | break; |
---|
348 | } |
---|
349 | |
---|
350 | /* |
---|
351 | * Historically, the readonly edit option was set per edit buffer in |
---|
352 | * vi, unless the -R command-line option was specified or the program |
---|
353 | * was executed as "view". (Well, to be truthful, if the letter 'w' |
---|
354 | * occurred anywhere in the program name, but let's not get into that.) |
---|
355 | * So, the persistant readonly state has to be stored in the screen |
---|
356 | * structure, and the edit option value toggles with the contents of |
---|
357 | * the edit buffer. If the persistant readonly flag is set, set the |
---|
358 | * readonly edit option. |
---|
359 | * |
---|
360 | * Otherwise, try and figure out if a file is readonly. This is a |
---|
361 | * dangerous thing to do. The kernel is the only arbiter of whether |
---|
362 | * or not a file is writeable, and the best that a user program can |
---|
363 | * do is guess. Obvious loopholes are files that are on a file system |
---|
364 | * mounted readonly (access catches this one on a few systems), or |
---|
365 | * alternate protection mechanisms, ACL's for example, that we can't |
---|
366 | * portably check. Lots of fun, and only here because users whined. |
---|
367 | * |
---|
368 | * !!! |
---|
369 | * Historic vi displayed the readonly message if none of the file |
---|
370 | * write bits were set, or if an an access(2) call on the path |
---|
371 | * failed. This seems reasonable. If the file is mode 444, root |
---|
372 | * users may want to know that the owner of the file did not expect |
---|
373 | * it to be written. |
---|
374 | * |
---|
375 | * Historic vi set the readonly bit if no write bits were set for |
---|
376 | * a file, even if the access call would have succeeded. This makes |
---|
377 | * the superuser force the write even when vi expects that it will |
---|
378 | * succeed. I'm less supportive of this semantic, but it's historic |
---|
379 | * practice and the conservative approach to vi'ing files as root. |
---|
380 | * |
---|
381 | * It would be nice if there was some way to update this when the user |
---|
382 | * does a "^Z; chmod ...". The problem is that we'd first have to |
---|
383 | * distinguish between readonly bits set because of file permissions |
---|
384 | * and those set for other reasons. That's not too hard, but deciding |
---|
385 | * when to reevaluate the permissions is trickier. An alternative |
---|
386 | * might be to turn off the readonly bit if the user forces a write |
---|
387 | * and it succeeds. |
---|
388 | * |
---|
389 | * XXX |
---|
390 | * Access(2) doesn't consider the effective uid/gid values. This |
---|
391 | * probably isn't a problem for vi when it's running standalone. |
---|
392 | */ |
---|
393 | if (readonly || F_ISSET(sp, SC_READONLY) || |
---|
394 | !F_ISSET(frp, FR_NEWFILE) && |
---|
395 | (!(sb.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)) || |
---|
396 | access(frp->name, W_OK))) |
---|
397 | O_SET(sp, O_READONLY); |
---|
398 | else |
---|
399 | O_CLR(sp, O_READONLY); |
---|
400 | |
---|
401 | /* Switch... */ |
---|
402 | ++ep->refcnt; |
---|
403 | sp->ep = ep; |
---|
404 | sp->frp = frp; |
---|
405 | |
---|
406 | /* Set the initial cursor position, queue initial command. */ |
---|
407 | file_cinit(sp); |
---|
408 | |
---|
409 | /* Redraw the screen from scratch, schedule a welcome message. */ |
---|
410 | F_SET(sp, SC_SCR_REFORMAT | SC_STATUS); |
---|
411 | |
---|
412 | return (0); |
---|
413 | |
---|
414 | err: if (frp->name != NULL) { |
---|
415 | free(frp->name); |
---|
416 | frp->name = NULL; |
---|
417 | } |
---|
418 | if (frp->tname != NULL) { |
---|
419 | (void)unlink(frp->tname); |
---|
420 | free(frp->tname); |
---|
421 | frp->tname = NULL; |
---|
422 | } |
---|
423 | |
---|
424 | oerr: if (F_ISSET(ep, F_RCV_ON)) |
---|
425 | (void)unlink(ep->rcv_path); |
---|
426 | if (ep->rcv_path != NULL) { |
---|
427 | free(ep->rcv_path); |
---|
428 | ep->rcv_path = NULL; |
---|
429 | } |
---|
430 | if (ep->db != NULL) |
---|
431 | (void)ep->db->close(ep->db); |
---|
432 | free(ep); |
---|
433 | |
---|
434 | return (open_err ? |
---|
435 | file_init(sp, frp, rcv_name, flags | FS_OPENERR) : 1); |
---|
436 | } |
---|
437 | |
---|
438 | /* |
---|
439 | * file_spath -- |
---|
440 | * Scan the user's path to find the file that we're going to |
---|
441 | * try and open. |
---|
442 | */ |
---|
443 | static int |
---|
444 | file_spath(sp, frp, sbp, existsp) |
---|
445 | SCR *sp; |
---|
446 | FREF *frp; |
---|
447 | struct stat *sbp; |
---|
448 | int *existsp; |
---|
449 | { |
---|
450 | CHAR_T savech; |
---|
451 | size_t len; |
---|
452 | int found; |
---|
453 | char *name, *p, *t, path[MAXPATHLEN]; |
---|
454 | |
---|
455 | /* |
---|
456 | * If the name is NULL or an explicit reference (i.e., the first |
---|
457 | * component is . or ..) ignore the O_PATH option. |
---|
458 | */ |
---|
459 | name = frp->name; |
---|
460 | if (name == NULL) { |
---|
461 | *existsp = 0; |
---|
462 | return (0); |
---|
463 | } |
---|
464 | if (name[0] == '/' || name[0] == '.' && |
---|
465 | (name[1] == '/' || name[1] == '.' && name[2] == '/')) { |
---|
466 | *existsp = !stat(name, sbp); |
---|
467 | return (0); |
---|
468 | } |
---|
469 | |
---|
470 | /* Try . */ |
---|
471 | if (!stat(name, sbp)) { |
---|
472 | *existsp = 1; |
---|
473 | return (0); |
---|
474 | } |
---|
475 | |
---|
476 | /* Try the O_PATH option values. */ |
---|
477 | for (found = 0, p = t = O_STR(sp, O_PATH);; ++p) |
---|
478 | if (*p == ':' || *p == '\0') { |
---|
479 | if (t < p - 1) { |
---|
480 | savech = *p; |
---|
481 | *p = '\0'; |
---|
482 | len = snprintf(path, |
---|
483 | sizeof(path), "%s/%s", t, name); |
---|
484 | *p = savech; |
---|
485 | if (!stat(path, sbp)) { |
---|
486 | found = 1; |
---|
487 | break; |
---|
488 | } |
---|
489 | } |
---|
490 | t = p + 1; |
---|
491 | if (*p == '\0') |
---|
492 | break; |
---|
493 | } |
---|
494 | |
---|
495 | /* If we found it, build a new pathname and discard the old one. */ |
---|
496 | if (found) { |
---|
497 | MALLOC_RET(sp, p, char *, len + 1); |
---|
498 | memcpy(p, path, len + 1); |
---|
499 | free(frp->name); |
---|
500 | frp->name = p; |
---|
501 | } |
---|
502 | *existsp = found; |
---|
503 | return (0); |
---|
504 | } |
---|
505 | |
---|
506 | /* |
---|
507 | * file_cinit -- |
---|
508 | * Set up the initial cursor position. |
---|
509 | */ |
---|
510 | static void |
---|
511 | file_cinit(sp) |
---|
512 | SCR *sp; |
---|
513 | { |
---|
514 | GS *gp; |
---|
515 | MARK m; |
---|
516 | size_t len; |
---|
517 | int nb; |
---|
518 | |
---|
519 | /* Set some basic defaults. */ |
---|
520 | sp->lno = 1; |
---|
521 | sp->cno = 0; |
---|
522 | |
---|
523 | /* |
---|
524 | * Historically, initial commands (the -c option) weren't executed |
---|
525 | * until a file was loaded, e.g. "vi +10 nofile", followed by an |
---|
526 | * :edit or :tag command, would execute the +10 on the file loaded |
---|
527 | * by the subsequent command, (assuming that it existed). This |
---|
528 | * applied as well to files loaded using the tag commands, and we |
---|
529 | * follow that historic practice. Also, all initial commands were |
---|
530 | * ex commands and were always executed on the last line of the file. |
---|
531 | * |
---|
532 | * Otherwise, if no initial command for this file: |
---|
533 | * If in ex mode, move to the last line, first nonblank character. |
---|
534 | * If the file has previously been edited, move to the last known |
---|
535 | * position, and check it for validity. |
---|
536 | * Otherwise, move to the first line, first nonblank. |
---|
537 | * |
---|
538 | * This gets called by the file init code, because we may be in a |
---|
539 | * file of ex commands and we want to execute them from the right |
---|
540 | * location in the file. |
---|
541 | */ |
---|
542 | nb = 0; |
---|
543 | gp = sp->gp; |
---|
544 | if (gp->c_option != NULL && !F_ISSET(sp->frp, FR_NEWFILE)) { |
---|
545 | if (db_last(sp, &sp->lno)) |
---|
546 | return; |
---|
547 | if (sp->lno == 0) { |
---|
548 | sp->lno = 1; |
---|
549 | sp->cno = 0; |
---|
550 | } |
---|
551 | if (ex_run_str(sp, |
---|
552 | "-c option", gp->c_option, strlen(gp->c_option), 1, 1)) |
---|
553 | return; |
---|
554 | gp->c_option = NULL; |
---|
555 | } else if (F_ISSET(sp, SC_EX)) { |
---|
556 | if (db_last(sp, &sp->lno)) |
---|
557 | return; |
---|
558 | if (sp->lno == 0) { |
---|
559 | sp->lno = 1; |
---|
560 | sp->cno = 0; |
---|
561 | return; |
---|
562 | } |
---|
563 | nb = 1; |
---|
564 | } else { |
---|
565 | if (F_ISSET(sp->frp, FR_CURSORSET)) { |
---|
566 | sp->lno = sp->frp->lno; |
---|
567 | sp->cno = sp->frp->cno; |
---|
568 | |
---|
569 | /* If returning to a file in vi, center the line. */ |
---|
570 | F_SET(sp, SC_SCR_CENTER); |
---|
571 | } else { |
---|
572 | if (O_ISSET(sp, O_COMMENT)) |
---|
573 | file_comment(sp); |
---|
574 | else |
---|
575 | sp->lno = 1; |
---|
576 | nb = 1; |
---|
577 | } |
---|
578 | if (db_get(sp, sp->lno, 0, NULL, &len)) { |
---|
579 | sp->lno = 1; |
---|
580 | sp->cno = 0; |
---|
581 | return; |
---|
582 | } |
---|
583 | if (!nb && sp->cno > len) |
---|
584 | nb = 1; |
---|
585 | } |
---|
586 | if (nb) { |
---|
587 | sp->cno = 0; |
---|
588 | (void)nonblank(sp, sp->lno, &sp->cno); |
---|
589 | } |
---|
590 | |
---|
591 | /* |
---|
592 | * !!! |
---|
593 | * The initial column is also the most attractive column. |
---|
594 | */ |
---|
595 | sp->rcm = sp->cno; |
---|
596 | |
---|
597 | /* |
---|
598 | * !!! |
---|
599 | * Historically, vi initialized the absolute mark, but ex did not. |
---|
600 | * Which meant, that if the first command in ex mode was "visual", |
---|
601 | * or if an ex command was executed first (e.g. vi +10 file) vi was |
---|
602 | * entered without the mark being initialized. For consistency, if |
---|
603 | * the file isn't empty, we initialize it for everyone, believing |
---|
604 | * that it can't hurt, and is generally useful. Not initializing it |
---|
605 | * if the file is empty is historic practice, although it has always |
---|
606 | * been possible to set (and use) marks in empty vi files. |
---|
607 | */ |
---|
608 | m.lno = sp->lno; |
---|
609 | m.cno = sp->cno; |
---|
610 | (void)mark_set(sp, ABSMARK1, &m, 0); |
---|
611 | } |
---|
612 | |
---|
613 | /* |
---|
614 | * file_end -- |
---|
615 | * Stop editing a file. |
---|
616 | * |
---|
617 | * PUBLIC: int file_end __P((SCR *, EXF *, int)); |
---|
618 | */ |
---|
619 | int |
---|
620 | file_end(sp, ep, force) |
---|
621 | SCR *sp; |
---|
622 | EXF *ep; |
---|
623 | int force; |
---|
624 | { |
---|
625 | FREF *frp; |
---|
626 | |
---|
627 | /* |
---|
628 | * !!! |
---|
629 | * ep MAY NOT BE THE SAME AS sp->ep, DON'T USE THE LATTER. |
---|
630 | * (If argument ep is NULL, use sp->ep.) |
---|
631 | * |
---|
632 | * If multiply referenced, just decrement the count and return. |
---|
633 | */ |
---|
634 | if (ep == NULL) |
---|
635 | ep = sp->ep; |
---|
636 | if (--ep->refcnt != 0) |
---|
637 | return (0); |
---|
638 | |
---|
639 | /* |
---|
640 | * |
---|
641 | * Clean up the FREF structure. |
---|
642 | * |
---|
643 | * Save the cursor location. |
---|
644 | * |
---|
645 | * XXX |
---|
646 | * It would be cleaner to do this somewhere else, but by the time |
---|
647 | * ex or vi knows that we're changing files it's already happened. |
---|
648 | */ |
---|
649 | frp = sp->frp; |
---|
650 | frp->lno = sp->lno; |
---|
651 | frp->cno = sp->cno; |
---|
652 | F_SET(frp, FR_CURSORSET); |
---|
653 | |
---|
654 | /* |
---|
655 | * We may no longer need the temporary backing file, so clean it |
---|
656 | * up. We don't need the FREF structure either, if the file was |
---|
657 | * never named, so lose it. |
---|
658 | * |
---|
659 | * !!! |
---|
660 | * Re: FR_DONTDELETE, see the comment above in file_init(). |
---|
661 | */ |
---|
662 | if (!F_ISSET(frp, FR_DONTDELETE) && frp->tname != NULL) { |
---|
663 | if (unlink(frp->tname)) |
---|
664 | msgq_str(sp, M_SYSERR, frp->tname, "240|%s: remove"); |
---|
665 | free(frp->tname); |
---|
666 | frp->tname = NULL; |
---|
667 | if (F_ISSET(frp, FR_TMPFILE)) { |
---|
668 | CIRCLEQ_REMOVE(&sp->gp->frefq, frp, q); |
---|
669 | if (frp->name != NULL) |
---|
670 | free(frp->name); |
---|
671 | free(frp); |
---|
672 | } |
---|
673 | sp->frp = NULL; |
---|
674 | } |
---|
675 | |
---|
676 | /* |
---|
677 | * Clean up the EXF structure. |
---|
678 | * |
---|
679 | * Close the db structure. |
---|
680 | */ |
---|
681 | if (ep->db->close != NULL && ep->db->close(ep->db) && !force) { |
---|
682 | msgq_str(sp, M_SYSERR, frp->name, "241|%s: close"); |
---|
683 | ++ep->refcnt; |
---|
684 | return (1); |
---|
685 | } |
---|
686 | |
---|
687 | /* COMMITTED TO THE CLOSE. THERE'S NO GOING BACK... */ |
---|
688 | |
---|
689 | /* Stop logging. */ |
---|
690 | (void)log_end(sp, ep); |
---|
691 | |
---|
692 | /* Free up any marks. */ |
---|
693 | (void)mark_end(sp, ep); |
---|
694 | |
---|
695 | /* |
---|
696 | * Delete recovery files, close the open descriptor, free recovery |
---|
697 | * memory. See recover.c for a description of the protocol. |
---|
698 | * |
---|
699 | * XXX |
---|
700 | * Unlink backup file first, we can detect that the recovery file |
---|
701 | * doesn't reference anything when the user tries to recover it. |
---|
702 | * There's a race, here, obviously, but it's fairly small. |
---|
703 | */ |
---|
704 | if (!F_ISSET(ep, F_RCV_NORM)) { |
---|
705 | if (ep->rcv_path != NULL && unlink(ep->rcv_path)) |
---|
706 | msgq_str(sp, M_SYSERR, ep->rcv_path, "242|%s: remove"); |
---|
707 | if (ep->rcv_mpath != NULL && unlink(ep->rcv_mpath)) |
---|
708 | msgq_str(sp, M_SYSERR, ep->rcv_mpath, "243|%s: remove"); |
---|
709 | } |
---|
710 | if (ep->fcntl_fd != -1) |
---|
711 | (void)close(ep->fcntl_fd); |
---|
712 | if (ep->rcv_fd != -1) |
---|
713 | (void)close(ep->rcv_fd); |
---|
714 | if (ep->rcv_path != NULL) |
---|
715 | free(ep->rcv_path); |
---|
716 | if (ep->rcv_mpath != NULL) |
---|
717 | free(ep->rcv_mpath); |
---|
718 | |
---|
719 | free(ep); |
---|
720 | return (0); |
---|
721 | } |
---|
722 | |
---|
723 | /* |
---|
724 | * file_write -- |
---|
725 | * Write the file to disk. Historic vi had fairly convoluted |
---|
726 | * semantics for whether or not writes would happen. That's |
---|
727 | * why all the flags. |
---|
728 | * |
---|
729 | * PUBLIC: int file_write __P((SCR *, MARK *, MARK *, char *, int)); |
---|
730 | */ |
---|
731 | int |
---|
732 | file_write(sp, fm, tm, name, flags) |
---|
733 | SCR *sp; |
---|
734 | MARK *fm, *tm; |
---|
735 | char *name; |
---|
736 | int flags; |
---|
737 | { |
---|
738 | enum { NEWFILE, OLDFILE } mtype; |
---|
739 | struct stat sb; |
---|
740 | EXF *ep; |
---|
741 | FILE *fp; |
---|
742 | FREF *frp; |
---|
743 | MARK from, to; |
---|
744 | size_t len; |
---|
745 | u_long nlno, nch; |
---|
746 | int fd, nf, noname, oflags, rval; |
---|
747 | char *p, *s, *t, buf[MAXPATHLEN + 64]; |
---|
748 | const char *msgstr; |
---|
749 | |
---|
750 | ep = sp->ep; |
---|
751 | frp = sp->frp; |
---|
752 | |
---|
753 | /* |
---|
754 | * Writing '%', or naming the current file explicitly, has the |
---|
755 | * same semantics as writing without a name. |
---|
756 | */ |
---|
757 | if (name == NULL || !strcmp(name, frp->name)) { |
---|
758 | noname = 1; |
---|
759 | name = frp->name; |
---|
760 | } else |
---|
761 | noname = 0; |
---|
762 | |
---|
763 | /* Can't write files marked read-only, unless forced. */ |
---|
764 | if (!LF_ISSET(FS_FORCE) && noname && O_ISSET(sp, O_READONLY)) { |
---|
765 | msgq(sp, M_ERR, LF_ISSET(FS_POSSIBLE) ? |
---|
766 | "244|Read-only file, not written; use ! to override" : |
---|
767 | "245|Read-only file, not written"); |
---|
768 | return (1); |
---|
769 | } |
---|
770 | |
---|
771 | /* If not forced, not appending, and "writeany" not set ... */ |
---|
772 | if (!LF_ISSET(FS_FORCE | FS_APPEND) && !O_ISSET(sp, O_WRITEANY)) { |
---|
773 | /* Don't overwrite anything but the original file. */ |
---|
774 | if ((!noname || F_ISSET(frp, FR_NAMECHANGE)) && |
---|
775 | !stat(name, &sb)) { |
---|
776 | msgq_str(sp, M_ERR, name, |
---|
777 | LF_ISSET(FS_POSSIBLE) ? |
---|
778 | "246|%s exists, not written; use ! to override" : |
---|
779 | "247|%s exists, not written"); |
---|
780 | return (1); |
---|
781 | } |
---|
782 | |
---|
783 | /* |
---|
784 | * Don't write part of any existing file. Only test for the |
---|
785 | * original file, the previous test catches anything else. |
---|
786 | */ |
---|
787 | if (!LF_ISSET(FS_ALL) && noname && !stat(name, &sb)) { |
---|
788 | msgq(sp, M_ERR, LF_ISSET(FS_POSSIBLE) ? |
---|
789 | "248|Partial file, not written; use ! to override" : |
---|
790 | "249|Partial file, not written"); |
---|
791 | return (1); |
---|
792 | } |
---|
793 | } |
---|
794 | |
---|
795 | /* |
---|
796 | * Figure out if the file already exists -- if it doesn't, we display |
---|
797 | * the "new file" message. The stat might not be necessary, but we |
---|
798 | * just repeat it because it's easier than hacking the previous tests. |
---|
799 | * The information is only used for the user message and modification |
---|
800 | * time test, so we can ignore the obvious race condition. |
---|
801 | * |
---|
802 | * One final test. If we're not forcing or appending the current file, |
---|
803 | * and we have a saved modification time, object if the file changed |
---|
804 | * since we last edited or wrote it, and make them force it. |
---|
805 | */ |
---|
806 | if (stat(name, &sb)) |
---|
807 | mtype = NEWFILE; |
---|
808 | else { |
---|
809 | if (noname && !LF_ISSET(FS_FORCE | FS_APPEND) && |
---|
810 | (F_ISSET(ep, F_DEVSET) && |
---|
811 | (sb.st_dev != ep->mdev || sb.st_ino != ep->minode) || |
---|
812 | sb.st_mtime != ep->mtime)) { |
---|
813 | msgq_str(sp, M_ERR, name, LF_ISSET(FS_POSSIBLE) ? |
---|
814 | "250|%s: file modified more recently than this copy; use ! to override" : |
---|
815 | "251|%s: file modified more recently than this copy"); |
---|
816 | return (1); |
---|
817 | } |
---|
818 | |
---|
819 | mtype = OLDFILE; |
---|
820 | } |
---|
821 | |
---|
822 | /* Set flags to create, write, and either append or truncate. */ |
---|
823 | oflags = O_CREAT | O_WRONLY | |
---|
824 | (LF_ISSET(FS_APPEND) ? O_APPEND : O_TRUNC); |
---|
825 | |
---|
826 | /* Backup the file if requested. */ |
---|
827 | if (!opts_empty(sp, O_BACKUP, 1) && |
---|
828 | file_backup(sp, name, O_STR(sp, O_BACKUP)) && !LF_ISSET(FS_FORCE)) |
---|
829 | return (1); |
---|
830 | |
---|
831 | /* Open the file. */ |
---|
832 | SIGBLOCK; |
---|
833 | if ((fd = open(name, oflags, |
---|
834 | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)) < 0) { |
---|
835 | msgq_str(sp, M_SYSERR, name, "%s"); |
---|
836 | SIGUNBLOCK; |
---|
837 | return (1); |
---|
838 | } |
---|
839 | SIGUNBLOCK; |
---|
840 | |
---|
841 | /* Try and get a lock. */ |
---|
842 | if (!noname && file_lock(sp, NULL, NULL, fd, 0) == LOCK_UNAVAIL) |
---|
843 | msgq_str(sp, M_ERR, name, |
---|
844 | "252|%s: write lock was unavailable"); |
---|
845 | |
---|
846 | #if __linux__ |
---|
847 | /* |
---|
848 | * XXX |
---|
849 | * In libc 4.5.x, fdopen(fd, "w") clears the O_APPEND flag (if set). |
---|
850 | * This bug is fixed in libc 4.6.x. |
---|
851 | * |
---|
852 | * This code works around this problem for libc 4.5.x users. |
---|
853 | * Note that this code is harmless if you're using libc 4.6.x. |
---|
854 | */ |
---|
855 | if (LF_ISSET(FS_APPEND) && lseek(fd, (off_t)0, SEEK_END) < 0) { |
---|
856 | msgq(sp, M_SYSERR, name); |
---|
857 | return (1); |
---|
858 | } |
---|
859 | #endif |
---|
860 | |
---|
861 | /* |
---|
862 | * Use stdio for buffering. |
---|
863 | * |
---|
864 | * XXX |
---|
865 | * SVR4.2 requires the fdopen mode exactly match the original open |
---|
866 | * mode, i.e. you have to open with "a" if appending. |
---|
867 | */ |
---|
868 | if ((fp = fdopen(fd, LF_ISSET(FS_APPEND) ? "a" : "w")) == NULL) { |
---|
869 | msgq_str(sp, M_SYSERR, name, "%s"); |
---|
870 | (void)close(fd); |
---|
871 | return (1); |
---|
872 | } |
---|
873 | |
---|
874 | /* Build fake addresses, if necessary. */ |
---|
875 | if (fm == NULL) { |
---|
876 | from.lno = 1; |
---|
877 | from.cno = 0; |
---|
878 | fm = &from; |
---|
879 | if (db_last(sp, &to.lno)) |
---|
880 | return (1); |
---|
881 | to.cno = 0; |
---|
882 | tm = &to; |
---|
883 | } |
---|
884 | |
---|
885 | rval = ex_writefp(sp, name, fp, fm, tm, &nlno, &nch, 0); |
---|
886 | |
---|
887 | /* |
---|
888 | * Save the new last modification time -- even if the write fails |
---|
889 | * we re-init the time. That way the user can clean up the disk |
---|
890 | * and rewrite without having to force it. |
---|
891 | */ |
---|
892 | if (noname) |
---|
893 | if (stat(name, &sb)) |
---|
894 | time(&ep->mtime); |
---|
895 | else { |
---|
896 | F_SET(ep, F_DEVSET); |
---|
897 | ep->mdev = sb.st_dev; |
---|
898 | ep->minode = sb.st_ino; |
---|
899 | |
---|
900 | ep->mtime = sb.st_mtime; |
---|
901 | } |
---|
902 | |
---|
903 | /* |
---|
904 | * If the write failed, complain loudly. ex_writefp() has already |
---|
905 | * complained about the actual error, reinforce it if data was lost. |
---|
906 | */ |
---|
907 | if (rval) { |
---|
908 | if (!LF_ISSET(FS_APPEND)) |
---|
909 | msgq_str(sp, M_ERR, name, |
---|
910 | "254|%s: WARNING: FILE TRUNCATED"); |
---|
911 | return (1); |
---|
912 | } |
---|
913 | |
---|
914 | /* |
---|
915 | * Once we've actually written the file, it doesn't matter that the |
---|
916 | * file name was changed -- if it was, we've already whacked it. |
---|
917 | */ |
---|
918 | F_CLR(frp, FR_NAMECHANGE); |
---|
919 | |
---|
920 | /* |
---|
921 | * If wrote the entire file, and it wasn't by appending it to a file, |
---|
922 | * clear the modified bit. If the file was written to the original |
---|
923 | * file name and the file is a temporary, set the "no exit" bit. This |
---|
924 | * permits the user to write the file and use it in the context of the |
---|
925 | * filesystem, but still keeps them from discarding their changes by |
---|
926 | * exiting. |
---|
927 | */ |
---|
928 | if (LF_ISSET(FS_ALL) && !LF_ISSET(FS_APPEND)) { |
---|
929 | F_CLR(ep, F_MODIFIED); |
---|
930 | if (F_ISSET(frp, FR_TMPFILE)) |
---|
931 | if (noname) |
---|
932 | F_SET(frp, FR_TMPEXIT); |
---|
933 | else |
---|
934 | F_CLR(frp, FR_TMPEXIT); |
---|
935 | } |
---|
936 | |
---|
937 | p = msg_print(sp, name, &nf); |
---|
938 | switch (mtype) { |
---|
939 | case NEWFILE: |
---|
940 | msgstr = msg_cat(sp, |
---|
941 | "256|%s: new file: %lu lines, %lu characters", NULL); |
---|
942 | len = snprintf(buf, sizeof(buf), msgstr, p, nlno, nch); |
---|
943 | break; |
---|
944 | case OLDFILE: |
---|
945 | msgstr = msg_cat(sp, LF_ISSET(FS_APPEND) ? |
---|
946 | "315|%s: appended: %lu lines, %lu characters" : |
---|
947 | "257|%s: %lu lines, %lu characters", NULL); |
---|
948 | len = snprintf(buf, sizeof(buf), msgstr, p, nlno, nch); |
---|
949 | break; |
---|
950 | default: |
---|
951 | abort(); |
---|
952 | } |
---|
953 | |
---|
954 | /* |
---|
955 | * There's a nasty problem with long path names. Cscope and tags files |
---|
956 | * can result in long paths and vi will request a continuation key from |
---|
957 | * the user. Unfortunately, the user has typed ahead, and chaos will |
---|
958 | * result. If we assume that the characters in the filenames only take |
---|
959 | * a single screen column each, we can trim the filename. |
---|
960 | */ |
---|
961 | s = buf; |
---|
962 | if (len >= sp->cols) { |
---|
963 | for (s = buf, t = buf + strlen(p); s < t && |
---|
964 | (*s != '/' || len >= sp->cols - 3); ++s, --len); |
---|
965 | if (s == t) |
---|
966 | s = buf; |
---|
967 | else { |
---|
968 | *--s = '.'; /* Leading ellipses. */ |
---|
969 | *--s = '.'; |
---|
970 | *--s = '.'; |
---|
971 | } |
---|
972 | } |
---|
973 | msgq(sp, M_INFO, s); |
---|
974 | if (nf) |
---|
975 | FREE_SPACE(sp, p, 0); |
---|
976 | return (0); |
---|
977 | } |
---|
978 | |
---|
979 | /* |
---|
980 | * file_backup -- |
---|
981 | * Backup the about-to-be-written file. |
---|
982 | * |
---|
983 | * XXX |
---|
984 | * We do the backup by copying the entire file. It would be nice to do |
---|
985 | * a rename instead, but: (1) both files may not fit and we want to fail |
---|
986 | * before doing the rename; (2) the backup file may not be on the same |
---|
987 | * disk partition as the file being written; (3) there may be optional |
---|
988 | * file information (MACs, DACs, whatever) that we won't get right if we |
---|
989 | * recreate the file. So, let's not risk it. |
---|
990 | */ |
---|
991 | static int |
---|
992 | file_backup(sp, name, bname) |
---|
993 | SCR *sp; |
---|
994 | char *name, *bname; |
---|
995 | { |
---|
996 | struct dirent *dp; |
---|
997 | struct stat sb; |
---|
998 | DIR *dirp; |
---|
999 | EXCMD cmd; |
---|
1000 | off_t off; |
---|
1001 | size_t blen; |
---|
1002 | int flags, maxnum, nr, num, nw, rfd, wfd, version; |
---|
1003 | char *bp, *estr, *p, *pct, *slash, *t, *wfname, buf[8192]; |
---|
1004 | |
---|
1005 | rfd = wfd = -1; |
---|
1006 | bp = estr = wfname = NULL; |
---|
1007 | |
---|
1008 | /* |
---|
1009 | * Open the current file for reading. Do this first, so that |
---|
1010 | * we don't exec a shell before the most likely failure point. |
---|
1011 | * If it doesn't exist, it's okay, there's just nothing to back |
---|
1012 | * up. |
---|
1013 | */ |
---|
1014 | errno = 0; |
---|
1015 | if ((rfd = open(name, O_RDONLY, 0)) < 0) { |
---|
1016 | if (errno == ENOENT) |
---|
1017 | return (0); |
---|
1018 | estr = name; |
---|
1019 | goto err; |
---|
1020 | } |
---|
1021 | |
---|
1022 | /* |
---|
1023 | * If the name starts with an 'N' character, add a version number |
---|
1024 | * to the name. Strip the leading N from the string passed to the |
---|
1025 | * expansion routines, for no particular reason. It would be nice |
---|
1026 | * to permit users to put the version number anywhere in the backup |
---|
1027 | * name, but there isn't a special character that we can use in the |
---|
1028 | * name, and giving a new character a special meaning leads to ugly |
---|
1029 | * hacks both here and in the supporting ex routines. |
---|
1030 | * |
---|
1031 | * Shell and file name expand the option's value. |
---|
1032 | */ |
---|
1033 | argv_init(sp, &cmd); |
---|
1034 | ex_cinit(&cmd, 0, 0, 0, 0, 0, NULL); |
---|
1035 | if (bname[0] == 'N') { |
---|
1036 | version = 1; |
---|
1037 | ++bname; |
---|
1038 | } else |
---|
1039 | version = 0; |
---|
1040 | if (argv_exp2(sp, &cmd, bname, strlen(bname))) |
---|
1041 | return (1); |
---|
1042 | |
---|
1043 | /* |
---|
1044 | * 0 args: impossible. |
---|
1045 | * 1 args: use it. |
---|
1046 | * >1 args: object, too many args. |
---|
1047 | */ |
---|
1048 | if (cmd.argc != 1) { |
---|
1049 | msgq_str(sp, M_ERR, bname, |
---|
1050 | "258|%s expanded into too many file names"); |
---|
1051 | (void)close(rfd); |
---|
1052 | return (1); |
---|
1053 | } |
---|
1054 | |
---|
1055 | /* |
---|
1056 | * If appending a version number, read through the directory, looking |
---|
1057 | * for file names that match the name followed by a number. Make all |
---|
1058 | * of the other % characters in name literal, so the user doesn't get |
---|
1059 | * surprised and sscanf doesn't drop core indirecting through pointers |
---|
1060 | * that don't exist. If any such files are found, increment its number |
---|
1061 | * by one. |
---|
1062 | */ |
---|
1063 | if (version) { |
---|
1064 | GET_SPACE_GOTO(sp, bp, blen, cmd.argv[0]->len * 2 + 50); |
---|
1065 | for (t = bp, slash = NULL, |
---|
1066 | p = cmd.argv[0]->bp; p[0] != '\0'; *t++ = *p++) |
---|
1067 | if (p[0] == '%') { |
---|
1068 | if (p[1] != '%') |
---|
1069 | *t++ = '%'; |
---|
1070 | } else if (p[0] == '/') |
---|
1071 | slash = t; |
---|
1072 | pct = t; |
---|
1073 | *t++ = '%'; |
---|
1074 | *t++ = 'd'; |
---|
1075 | *t = '\0'; |
---|
1076 | |
---|
1077 | if (slash == NULL) { |
---|
1078 | dirp = opendir("."); |
---|
1079 | p = bp; |
---|
1080 | } else { |
---|
1081 | *slash = '\0'; |
---|
1082 | dirp = opendir(bp); |
---|
1083 | *slash = '/'; |
---|
1084 | p = slash + 1; |
---|
1085 | } |
---|
1086 | if (dirp == NULL) { |
---|
1087 | estr = cmd.argv[0]->bp; |
---|
1088 | goto err; |
---|
1089 | } |
---|
1090 | |
---|
1091 | for (maxnum = 0; (dp = readdir(dirp)) != NULL;) |
---|
1092 | if (sscanf(dp->d_name, p, &num) == 1 && num > maxnum) |
---|
1093 | maxnum = num; |
---|
1094 | (void)closedir(dirp); |
---|
1095 | |
---|
1096 | /* Format the backup file name. */ |
---|
1097 | (void)snprintf(pct, blen - (pct - bp), "%d", maxnum + 1); |
---|
1098 | wfname = bp; |
---|
1099 | } else { |
---|
1100 | bp = NULL; |
---|
1101 | wfname = cmd.argv[0]->bp; |
---|
1102 | } |
---|
1103 | |
---|
1104 | /* Open the backup file, avoiding lurkers. */ |
---|
1105 | if (stat(wfname, &sb) == 0) { |
---|
1106 | if (!S_ISREG(sb.st_mode)) { |
---|
1107 | msgq_str(sp, M_ERR, bname, |
---|
1108 | "259|%s: not a regular file"); |
---|
1109 | goto err; |
---|
1110 | } |
---|
1111 | if (sb.st_uid != getuid()) { |
---|
1112 | msgq_str(sp, M_ERR, bname, "260|%s: not owned by you"); |
---|
1113 | goto err; |
---|
1114 | } |
---|
1115 | if (sb.st_mode & (S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)) { |
---|
1116 | msgq_str(sp, M_ERR, bname, |
---|
1117 | "261|%s: accessible by a user other than the owner"); |
---|
1118 | goto err; |
---|
1119 | } |
---|
1120 | flags = O_TRUNC; |
---|
1121 | } else |
---|
1122 | flags = O_CREAT | O_EXCL; |
---|
1123 | if ((wfd = open(wfname, flags | O_WRONLY, S_IRUSR | S_IWUSR)) < 0) { |
---|
1124 | estr = bname; |
---|
1125 | goto err; |
---|
1126 | } |
---|
1127 | |
---|
1128 | /* Copy the file's current contents to its backup value. */ |
---|
1129 | while ((nr = read(rfd, buf, sizeof(buf))) > 0) |
---|
1130 | for (off = 0; nr != 0; nr -= nw, off += nw) |
---|
1131 | if ((nw = write(wfd, buf + off, nr)) < 0) { |
---|
1132 | estr = wfname; |
---|
1133 | goto err; |
---|
1134 | } |
---|
1135 | if (nr < 0) { |
---|
1136 | estr = name; |
---|
1137 | goto err; |
---|
1138 | } |
---|
1139 | |
---|
1140 | if (close(rfd)) { |
---|
1141 | estr = name; |
---|
1142 | goto err; |
---|
1143 | } |
---|
1144 | if (close(wfd)) { |
---|
1145 | estr = wfname; |
---|
1146 | goto err; |
---|
1147 | } |
---|
1148 | if (bp != NULL) |
---|
1149 | FREE_SPACE(sp, bp, blen); |
---|
1150 | return (0); |
---|
1151 | |
---|
1152 | alloc_err: |
---|
1153 | err: if (rfd != -1) |
---|
1154 | (void)close(rfd); |
---|
1155 | if (wfd != -1) { |
---|
1156 | (void)unlink(wfname); |
---|
1157 | (void)close(wfd); |
---|
1158 | } |
---|
1159 | if (estr) |
---|
1160 | msgq_str(sp, M_SYSERR, estr, "%s"); |
---|
1161 | if (bp != NULL) |
---|
1162 | FREE_SPACE(sp, bp, blen); |
---|
1163 | return (1); |
---|
1164 | } |
---|
1165 | |
---|
1166 | /* |
---|
1167 | * file_comment -- |
---|
1168 | * Skip the first comment. |
---|
1169 | */ |
---|
1170 | static void |
---|
1171 | file_comment(sp) |
---|
1172 | SCR *sp; |
---|
1173 | { |
---|
1174 | recno_t lno; |
---|
1175 | size_t len; |
---|
1176 | char *p; |
---|
1177 | |
---|
1178 | for (lno = 1; !db_get(sp, lno, 0, &p, &len) && len == 0; ++lno); |
---|
1179 | if (p == NULL) |
---|
1180 | return; |
---|
1181 | if (p[0] == '#') { |
---|
1182 | F_SET(sp, SC_SCR_TOP); |
---|
1183 | while (!db_get(sp, ++lno, 0, &p, &len)) |
---|
1184 | if (len < 1 || p[0] != '#') { |
---|
1185 | sp->lno = lno; |
---|
1186 | return; |
---|
1187 | } |
---|
1188 | } else if (len > 1 && p[0] == '/' && p[1] == '*') { |
---|
1189 | F_SET(sp, SC_SCR_TOP); |
---|
1190 | do { |
---|
1191 | for (; len > 1; --len, ++p) |
---|
1192 | if (p[0] == '*' && p[1] == '/') { |
---|
1193 | sp->lno = lno; |
---|
1194 | return; |
---|
1195 | } |
---|
1196 | } while (!db_get(sp, ++lno, 0, &p, &len)); |
---|
1197 | } else if (len > 1 && p[0] == '/' && p[1] == '/') { |
---|
1198 | F_SET(sp, SC_SCR_TOP); |
---|
1199 | p += 2; |
---|
1200 | len -= 2; |
---|
1201 | do { |
---|
1202 | for (; len > 1; --len, ++p) |
---|
1203 | if (p[0] == '/' && p[1] == '/') { |
---|
1204 | sp->lno = lno; |
---|
1205 | return; |
---|
1206 | } |
---|
1207 | } while (!db_get(sp, ++lno, 0, &p, &len)); |
---|
1208 | } |
---|
1209 | } |
---|
1210 | |
---|
1211 | /* |
---|
1212 | * file_m1 -- |
---|
1213 | * First modification check routine. The :next, :prev, :rewind, :tag, |
---|
1214 | * :tagpush, :tagpop, ^^ modifications check. |
---|
1215 | * |
---|
1216 | * PUBLIC: int file_m1 __P((SCR *, int, int)); |
---|
1217 | */ |
---|
1218 | int |
---|
1219 | file_m1(sp, force, flags) |
---|
1220 | SCR *sp; |
---|
1221 | int force, flags; |
---|
1222 | { |
---|
1223 | EXF *ep; |
---|
1224 | |
---|
1225 | ep = sp->ep; |
---|
1226 | |
---|
1227 | /* If no file loaded, return no modifications. */ |
---|
1228 | if (ep == NULL) |
---|
1229 | return (0); |
---|
1230 | |
---|
1231 | /* |
---|
1232 | * If the file has been modified, we'll want to write it back or |
---|
1233 | * fail. If autowrite is set, we'll write it back automatically, |
---|
1234 | * unless force is also set. Otherwise, we fail unless forced or |
---|
1235 | * there's another open screen on this file. |
---|
1236 | */ |
---|
1237 | if (F_ISSET(ep, F_MODIFIED)) |
---|
1238 | if (O_ISSET(sp, O_AUTOWRITE)) { |
---|
1239 | if (!force && file_aw(sp, flags)) |
---|
1240 | return (1); |
---|
1241 | } else if (ep->refcnt <= 1 && !force) { |
---|
1242 | msgq(sp, M_ERR, LF_ISSET(FS_POSSIBLE) ? |
---|
1243 | "262|File modified since last complete write; write or use ! to override" : |
---|
1244 | "263|File modified since last complete write; write or use :edit! to override"); |
---|
1245 | return (1); |
---|
1246 | } |
---|
1247 | |
---|
1248 | return (file_m3(sp, force)); |
---|
1249 | } |
---|
1250 | |
---|
1251 | /* |
---|
1252 | * file_m2 -- |
---|
1253 | * Second modification check routine. The :edit, :quit, :recover |
---|
1254 | * modifications check. |
---|
1255 | * |
---|
1256 | * PUBLIC: int file_m2 __P((SCR *, int)); |
---|
1257 | */ |
---|
1258 | int |
---|
1259 | file_m2(sp, force) |
---|
1260 | SCR *sp; |
---|
1261 | int force; |
---|
1262 | { |
---|
1263 | EXF *ep; |
---|
1264 | |
---|
1265 | ep = sp->ep; |
---|
1266 | |
---|
1267 | /* If no file loaded, return no modifications. */ |
---|
1268 | if (ep == NULL) |
---|
1269 | return (0); |
---|
1270 | |
---|
1271 | /* |
---|
1272 | * If the file has been modified, we'll want to fail, unless forced |
---|
1273 | * or there's another open screen on this file. |
---|
1274 | */ |
---|
1275 | if (F_ISSET(ep, F_MODIFIED) && ep->refcnt <= 1 && !force) { |
---|
1276 | msgq(sp, M_ERR, |
---|
1277 | "264|File modified since last complete write; write or use ! to override"); |
---|
1278 | return (1); |
---|
1279 | } |
---|
1280 | |
---|
1281 | return (file_m3(sp, force)); |
---|
1282 | } |
---|
1283 | |
---|
1284 | /* |
---|
1285 | * file_m3 -- |
---|
1286 | * Third modification check routine. |
---|
1287 | * |
---|
1288 | * PUBLIC: int file_m3 __P((SCR *, int)); |
---|
1289 | */ |
---|
1290 | int |
---|
1291 | file_m3(sp, force) |
---|
1292 | SCR *sp; |
---|
1293 | int force; |
---|
1294 | { |
---|
1295 | EXF *ep; |
---|
1296 | |
---|
1297 | ep = sp->ep; |
---|
1298 | |
---|
1299 | /* If no file loaded, return no modifications. */ |
---|
1300 | if (ep == NULL) |
---|
1301 | return (0); |
---|
1302 | |
---|
1303 | /* |
---|
1304 | * Don't exit while in a temporary files if the file was ever modified. |
---|
1305 | * The problem is that if the user does a ":wq", we write and quit, |
---|
1306 | * unlinking the temporary file. Not what the user had in mind at all. |
---|
1307 | * We permit writing to temporary files, so that user maps using file |
---|
1308 | * system names work with temporary files. |
---|
1309 | */ |
---|
1310 | if (F_ISSET(sp->frp, FR_TMPEXIT) && ep->refcnt <= 1 && !force) { |
---|
1311 | msgq(sp, M_ERR, |
---|
1312 | "265|File is a temporary; exit will discard modifications"); |
---|
1313 | return (1); |
---|
1314 | } |
---|
1315 | return (0); |
---|
1316 | } |
---|
1317 | |
---|
1318 | /* |
---|
1319 | * file_aw -- |
---|
1320 | * Autowrite routine. If modified, autowrite is set and the readonly bit |
---|
1321 | * is not set, write the file. A routine so there's a place to put the |
---|
1322 | * comment. |
---|
1323 | * |
---|
1324 | * PUBLIC: int file_aw __P((SCR *, int)); |
---|
1325 | */ |
---|
1326 | int |
---|
1327 | file_aw(sp, flags) |
---|
1328 | SCR *sp; |
---|
1329 | int flags; |
---|
1330 | { |
---|
1331 | if (!F_ISSET(sp->ep, F_MODIFIED)) |
---|
1332 | return (0); |
---|
1333 | if (!O_ISSET(sp, O_AUTOWRITE)) |
---|
1334 | return (0); |
---|
1335 | |
---|
1336 | /* |
---|
1337 | * !!! |
---|
1338 | * Historic 4BSD vi attempted to write the file if autowrite was set, |
---|
1339 | * regardless of the writeability of the file (as defined by the file |
---|
1340 | * readonly flag). System V changed this as some point, not attempting |
---|
1341 | * autowrite if the file was readonly. This feels like a bug fix to |
---|
1342 | * me (e.g. the principle of least surprise is violated if readonly is |
---|
1343 | * set and vi writes the file), so I'm compatible with System V. |
---|
1344 | */ |
---|
1345 | if (O_ISSET(sp, O_READONLY)) { |
---|
1346 | msgq(sp, M_INFO, |
---|
1347 | "266|File readonly, modifications not auto-written"); |
---|
1348 | return (1); |
---|
1349 | } |
---|
1350 | return (file_write(sp, NULL, NULL, NULL, flags)); |
---|
1351 | } |
---|
1352 | |
---|
1353 | /* |
---|
1354 | * set_alt_name -- |
---|
1355 | * Set the alternate pathname. |
---|
1356 | * |
---|
1357 | * Set the alternate pathname. It's a routine because I wanted some place |
---|
1358 | * to hang this comment. The alternate pathname (normally referenced using |
---|
1359 | * the special character '#' during file expansion and in the vi ^^ command) |
---|
1360 | * is set by almost all ex commands that take file names as arguments. The |
---|
1361 | * rules go something like this: |
---|
1362 | * |
---|
1363 | * 1: If any ex command takes a file name as an argument (except for the |
---|
1364 | * :next command), the alternate pathname is set to that file name. |
---|
1365 | * This excludes the command ":e" and ":w !command" as no file name |
---|
1366 | * was specified. Note, historically, the :source command did not set |
---|
1367 | * the alternate pathname. It does in nvi, for consistency. |
---|
1368 | * |
---|
1369 | * 2: However, if any ex command sets the current pathname, e.g. the |
---|
1370 | * ":e file" or ":rew" commands succeed, then the alternate pathname |
---|
1371 | * is set to the previous file's current pathname, if it had one. |
---|
1372 | * This includes the ":file" command and excludes the ":e" command. |
---|
1373 | * So, by rule #1 and rule #2, if ":edit foo" fails, the alternate |
---|
1374 | * pathname will be "foo", if it succeeds, the alternate pathname will |
---|
1375 | * be the previous current pathname. The ":e" command will not set |
---|
1376 | * the alternate or current pathnames regardless. |
---|
1377 | * |
---|
1378 | * 3: However, if it's a read or write command with a file argument and |
---|
1379 | * the current pathname has not yet been set, the file name becomes |
---|
1380 | * the current pathname, and the alternate pathname is unchanged. |
---|
1381 | * |
---|
1382 | * If the user edits a temporary file, there may be times when there is no |
---|
1383 | * alternative file name. A name argument of NULL turns it off. |
---|
1384 | * |
---|
1385 | * PUBLIC: void set_alt_name __P((SCR *, char *)); |
---|
1386 | */ |
---|
1387 | void |
---|
1388 | set_alt_name(sp, name) |
---|
1389 | SCR *sp; |
---|
1390 | char *name; |
---|
1391 | { |
---|
1392 | if (sp->alt_name != NULL) |
---|
1393 | free(sp->alt_name); |
---|
1394 | if (name == NULL) |
---|
1395 | sp->alt_name = NULL; |
---|
1396 | else if ((sp->alt_name = strdup(name)) == NULL) |
---|
1397 | msgq(sp, M_SYSERR, NULL); |
---|
1398 | } |
---|
1399 | |
---|
1400 | /* |
---|
1401 | * file_lock -- |
---|
1402 | * Get an exclusive lock on a file. |
---|
1403 | * |
---|
1404 | * XXX |
---|
1405 | * The default locking is flock(2) style, not fcntl(2). The latter is |
---|
1406 | * known to fail badly on some systems, and its only advantage is that |
---|
1407 | * it occasionally works over NFS. |
---|
1408 | * |
---|
1409 | * Furthermore, the semantics of fcntl(2) are wrong. The problems are |
---|
1410 | * two-fold: you can't close any file descriptor associated with the file |
---|
1411 | * without losing all of the locks, and you can't get an exclusive lock |
---|
1412 | * unless you have the file open for writing. Someone ought to be shot, |
---|
1413 | * but it's probably too late, they may already have reproduced. To get |
---|
1414 | * around these problems, nvi opens the files for writing when it can and |
---|
1415 | * acquires a second file descriptor when it can't. The recovery files |
---|
1416 | * are examples of the former, they're always opened for writing. The DB |
---|
1417 | * files can't be opened for writing because the semantics of DB are that |
---|
1418 | * files opened for writing are flushed back to disk when the DB session |
---|
1419 | * is ended. So, in that case we have to acquire an extra file descriptor. |
---|
1420 | * |
---|
1421 | * PUBLIC: lockr_t file_lock __P((SCR *, char *, int *, int, int)); |
---|
1422 | */ |
---|
1423 | lockr_t |
---|
1424 | file_lock(sp, name, fdp, fd, iswrite) |
---|
1425 | SCR *sp; |
---|
1426 | char *name; |
---|
1427 | int *fdp, fd, iswrite; |
---|
1428 | { |
---|
1429 | if (!O_ISSET(sp, O_LOCKFILES)) |
---|
1430 | return (LOCK_SUCCESS); |
---|
1431 | |
---|
1432 | #ifdef HAVE_LOCK_FLOCK /* Hurrah! We've got flock(2). */ |
---|
1433 | /* |
---|
1434 | * !!! |
---|
1435 | * We need to distinguish a lock not being available for the file |
---|
1436 | * from the file system not supporting locking. Flock is documented |
---|
1437 | * as returning EWOULDBLOCK; add EAGAIN for good measure, and assume |
---|
1438 | * they are the former. There's no portable way to do this. |
---|
1439 | */ |
---|
1440 | errno = 0; |
---|
1441 | return (flock(fd, LOCK_EX | LOCK_NB) ? errno == EAGAIN |
---|
1442 | #ifdef EWOULDBLOCK |
---|
1443 | || errno == EWOULDBLOCK |
---|
1444 | #endif |
---|
1445 | ? LOCK_UNAVAIL : LOCK_FAILED : LOCK_SUCCESS); |
---|
1446 | #endif |
---|
1447 | #ifdef HAVE_LOCK_FCNTL /* Gag me. We've got fcntl(2). */ |
---|
1448 | { |
---|
1449 | struct flock arg; |
---|
1450 | int didopen, sverrno; |
---|
1451 | |
---|
1452 | arg.l_type = F_WRLCK; |
---|
1453 | arg.l_whence = 0; /* SEEK_SET */ |
---|
1454 | arg.l_start = arg.l_len = 0; |
---|
1455 | arg.l_pid = 0; |
---|
1456 | |
---|
1457 | /* |
---|
1458 | * If the file descriptor isn't opened for writing, it must fail. |
---|
1459 | * If we fail because we can't get a read/write file descriptor, |
---|
1460 | * we return LOCK_SUCCESS, believing that the file is readonly |
---|
1461 | * and that will be sufficient to warn the user. |
---|
1462 | */ |
---|
1463 | if (!iswrite) { |
---|
1464 | if (name == NULL || fdp == NULL) |
---|
1465 | return (LOCK_FAILED); |
---|
1466 | if ((fd = open(name, O_RDWR, 0)) == -1) |
---|
1467 | return (LOCK_SUCCESS); |
---|
1468 | *fdp = fd; |
---|
1469 | didopen = 1; |
---|
1470 | } |
---|
1471 | |
---|
1472 | errno = 0; |
---|
1473 | if (!fcntl(fd, F_SETLK, &arg)) |
---|
1474 | return (LOCK_SUCCESS); |
---|
1475 | if (didopen) { |
---|
1476 | sverrno = errno; |
---|
1477 | (void)close(fd); |
---|
1478 | errno = sverrno; |
---|
1479 | } |
---|
1480 | |
---|
1481 | /* |
---|
1482 | * !!! |
---|
1483 | * We need to distinguish a lock not being available for the file |
---|
1484 | * from the file system not supporting locking. Fcntl is documented |
---|
1485 | * as returning EACCESS and EAGAIN; add EWOULDBLOCK for good measure, |
---|
1486 | * and assume they are the former. There's no portable way to do this. |
---|
1487 | */ |
---|
1488 | return (errno == EACCES || errno == EAGAIN |
---|
1489 | #ifdef EWOULDBLOCK |
---|
1490 | || errno == EWOULDBLOCK |
---|
1491 | #endif |
---|
1492 | ? LOCK_UNAVAIL : LOCK_FAILED); |
---|
1493 | } |
---|
1494 | #endif |
---|
1495 | #if !defined(HAVE_LOCK_FLOCK) && !defined(HAVE_LOCK_FCNTL) |
---|
1496 | return (LOCK_SUCCESS); |
---|
1497 | #endif |
---|
1498 | } |
---|