1 |
|
|
/* $OpenBSD: file.c,v 1.100 2016/01/02 10:39:19 lum Exp $ */ |
2 |
|
|
|
3 |
|
|
/* This file is in the public domain. */ |
4 |
|
|
|
5 |
|
|
/* |
6 |
|
|
* File commands. |
7 |
|
|
*/ |
8 |
|
|
|
9 |
|
|
#include <sys/queue.h> |
10 |
|
|
#include <sys/stat.h> |
11 |
|
|
#include <errno.h> |
12 |
|
|
#include <libgen.h> |
13 |
|
|
#include <signal.h> |
14 |
|
|
#include <stdio.h> |
15 |
|
|
#include <stdlib.h> |
16 |
|
|
#include <string.h> |
17 |
|
|
#include <unistd.h> |
18 |
|
|
|
19 |
|
|
#include "def.h" |
20 |
|
|
|
21 |
|
|
size_t xdirname(char *, const char *, size_t); |
22 |
|
|
|
23 |
|
|
/* |
24 |
|
|
* Insert a file into the current buffer. Real easy - just call the |
25 |
|
|
* insertfile routine with the file name. |
26 |
|
|
*/ |
27 |
|
|
/* ARGSUSED */ |
28 |
|
|
int |
29 |
|
|
fileinsert(int f, int n) |
30 |
|
|
{ |
31 |
|
|
char fname[NFILEN], *bufp, *adjf; |
32 |
|
|
|
33 |
|
|
if (getbufcwd(fname, sizeof(fname)) != TRUE) |
34 |
|
|
fname[0] = '\0'; |
35 |
|
|
bufp = eread("Insert file: ", fname, NFILEN, |
36 |
|
|
EFNEW | EFCR | EFFILE | EFDEF); |
37 |
|
|
if (bufp == NULL) |
38 |
|
|
return (ABORT); |
39 |
|
|
else if (bufp[0] == '\0') |
40 |
|
|
return (FALSE); |
41 |
|
|
adjf = adjustname(bufp, TRUE); |
42 |
|
|
if (adjf == NULL) |
43 |
|
|
return (FALSE); |
44 |
|
|
return (insertfile(adjf, NULL, FALSE)); |
45 |
|
|
} |
46 |
|
|
|
47 |
|
|
/* |
48 |
|
|
* Select a file for editing. If the file is a directory, invoke dired. |
49 |
|
|
* Otherwise, look around to see if you can find the file in another buffer; |
50 |
|
|
* if you can find it, just switch to the buffer. If you cannot find the |
51 |
|
|
* file, create a new buffer, read in the text, and switch to the new buffer. |
52 |
|
|
*/ |
53 |
|
|
/* ARGSUSED */ |
54 |
|
|
int |
55 |
|
|
filevisit(int f, int n) |
56 |
|
|
{ |
57 |
|
|
struct buffer *bp; |
58 |
|
|
char fname[NFILEN], *bufp, *adjf; |
59 |
|
|
int status; |
60 |
|
|
|
61 |
|
|
if (getbufcwd(fname, sizeof(fname)) != TRUE) |
62 |
|
|
fname[0] = '\0'; |
63 |
|
|
bufp = eread("Find file: ", fname, NFILEN, |
64 |
|
|
EFNEW | EFCR | EFFILE | EFDEF); |
65 |
|
|
if (bufp == NULL) |
66 |
|
|
return (ABORT); |
67 |
|
|
else if (bufp[0] == '\0') |
68 |
|
|
return (FALSE); |
69 |
|
|
adjf = adjustname(fname, TRUE); |
70 |
|
|
if (adjf == NULL) |
71 |
|
|
return (FALSE); |
72 |
|
|
if (fisdir(adjf) == TRUE) |
73 |
|
|
return (do_dired(adjf)); |
74 |
|
|
if ((bp = findbuffer(adjf)) == NULL) |
75 |
|
|
return (FALSE); |
76 |
|
|
curbp = bp; |
77 |
|
|
if (showbuffer(bp, curwp, WFFULL) != TRUE) |
78 |
|
|
return (FALSE); |
79 |
|
|
if (bp->b_fname[0] == '\0') { |
80 |
|
|
if ((status = readin(adjf)) != TRUE) |
81 |
|
|
killbuffer(bp); |
82 |
|
|
return (status); |
83 |
|
|
} |
84 |
|
|
return (TRUE); |
85 |
|
|
} |
86 |
|
|
|
87 |
|
|
/* |
88 |
|
|
* Replace the current file with an alternate one. Semantics for finding |
89 |
|
|
* the replacement file are the same as 'filevisit', except the current |
90 |
|
|
* buffer is killed before the switch. If the kill fails, or is aborted, |
91 |
|
|
* revert to the original file. |
92 |
|
|
*/ |
93 |
|
|
/* ARGSUSED */ |
94 |
|
|
int |
95 |
|
|
filevisitalt(int f, int n) |
96 |
|
|
{ |
97 |
|
|
char fname[NFILEN], *bufp; |
98 |
|
|
|
99 |
|
|
if (getbufcwd(fname, sizeof(fname)) != TRUE) |
100 |
|
|
fname[0] = '\0'; |
101 |
|
|
bufp = eread("Find alternate file: ", fname, NFILEN, |
102 |
|
|
EFNEW | EFCR | EFFILE | EFDEF); |
103 |
|
|
if (bufp == NULL) |
104 |
|
|
return (ABORT); |
105 |
|
|
else if (bufp[0] == '\0') |
106 |
|
|
return (FALSE); |
107 |
|
|
|
108 |
|
|
return (do_filevisitalt(fname)); |
109 |
|
|
} |
110 |
|
|
|
111 |
|
|
int |
112 |
|
|
do_filevisitalt(char *fn) |
113 |
|
|
{ |
114 |
|
|
struct buffer *bp; |
115 |
|
|
int status; |
116 |
|
|
char *adjf; |
117 |
|
|
|
118 |
|
|
status = killbuffer(curbp); |
119 |
|
|
if (status == ABORT || status == FALSE) |
120 |
|
|
return (ABORT); |
121 |
|
|
|
122 |
|
|
adjf = adjustname(fn, TRUE); |
123 |
|
|
if (adjf == NULL) |
124 |
|
|
return (FALSE); |
125 |
|
|
if (fisdir(adjf) == TRUE) |
126 |
|
|
return (do_dired(adjf)); |
127 |
|
|
if ((bp = findbuffer(adjf)) == NULL) |
128 |
|
|
return (FALSE); |
129 |
|
|
curbp = bp; |
130 |
|
|
if (showbuffer(bp, curwp, WFFULL) != TRUE) |
131 |
|
|
return (FALSE); |
132 |
|
|
if (bp->b_fname[0] == '\0') { |
133 |
|
|
if ((status = readin(adjf)) != TRUE) |
134 |
|
|
killbuffer(bp); |
135 |
|
|
return (status); |
136 |
|
|
} |
137 |
|
|
return (TRUE); |
138 |
|
|
} |
139 |
|
|
|
140 |
|
|
int |
141 |
|
|
filevisitro(int f, int n) |
142 |
|
|
{ |
143 |
|
|
int error; |
144 |
|
|
|
145 |
|
|
error = filevisit(f, n); |
146 |
|
|
if (error != TRUE) |
147 |
|
|
return (error); |
148 |
|
|
curbp->b_flag |= BFREADONLY; |
149 |
|
|
return (TRUE); |
150 |
|
|
} |
151 |
|
|
|
152 |
|
|
/* |
153 |
|
|
* Pop to a file in the other window. Same as the last function, but uses |
154 |
|
|
* popbuf instead of showbuffer. |
155 |
|
|
*/ |
156 |
|
|
/* ARGSUSED */ |
157 |
|
|
int |
158 |
|
|
poptofile(int f, int n) |
159 |
|
|
{ |
160 |
|
|
struct buffer *bp; |
161 |
|
|
struct mgwin *wp; |
162 |
|
|
char fname[NFILEN], *adjf, *bufp; |
163 |
|
|
int status; |
164 |
|
|
|
165 |
|
|
if (getbufcwd(fname, sizeof(fname)) != TRUE) |
166 |
|
|
fname[0] = '\0'; |
167 |
|
|
if ((bufp = eread("Find file in other window: ", fname, NFILEN, |
168 |
|
|
EFNEW | EFCR | EFFILE | EFDEF)) == NULL) |
169 |
|
|
return (ABORT); |
170 |
|
|
else if (bufp[0] == '\0') |
171 |
|
|
return (FALSE); |
172 |
|
|
adjf = adjustname(fname, TRUE); |
173 |
|
|
if (adjf == NULL) |
174 |
|
|
return (FALSE); |
175 |
|
|
if (fisdir(adjf) == TRUE) |
176 |
|
|
return (do_dired(adjf)); |
177 |
|
|
if ((bp = findbuffer(adjf)) == NULL) |
178 |
|
|
return (FALSE); |
179 |
|
|
if (bp == curbp) |
180 |
|
|
return (splitwind(f, n)); |
181 |
|
|
if ((wp = popbuf(bp, WNONE)) == NULL) |
182 |
|
|
return (FALSE); |
183 |
|
|
curbp = bp; |
184 |
|
|
curwp = wp; |
185 |
|
|
if (bp->b_fname[0] == '\0') { |
186 |
|
|
if ((status = readin(adjf)) != TRUE) |
187 |
|
|
killbuffer(bp); |
188 |
|
|
return (status); |
189 |
|
|
} |
190 |
|
|
return (TRUE); |
191 |
|
|
} |
192 |
|
|
|
193 |
|
|
/* |
194 |
|
|
* Read the file "fname" into the current buffer. Make all of the text |
195 |
|
|
* in the buffer go away, after checking for unsaved changes. This is |
196 |
|
|
* called by the "read" command, the "visit" command, and the mainline |
197 |
|
|
* (for "mg file"). |
198 |
|
|
*/ |
199 |
|
|
int |
200 |
|
|
readin(char *fname) |
201 |
|
|
{ |
202 |
|
|
struct mgwin *wp; |
203 |
|
|
struct stat statbuf; |
204 |
|
|
int status, i, ro = FALSE; |
205 |
|
|
PF *ael; |
206 |
|
|
char dp[NFILEN]; |
207 |
|
|
|
208 |
|
|
/* might be old */ |
209 |
|
|
if (bclear(curbp) != TRUE) |
210 |
|
|
return (TRUE); |
211 |
|
|
/* Clear readonly. May be set by autoexec path */ |
212 |
|
|
curbp->b_flag &= ~BFREADONLY; |
213 |
|
|
if ((status = insertfile(fname, fname, TRUE)) != TRUE) { |
214 |
|
|
dobeep(); |
215 |
|
|
ewprintf("File is not readable: %s", fname); |
216 |
|
|
return (FALSE); |
217 |
|
|
} |
218 |
|
|
|
219 |
|
|
for (wp = wheadp; wp != NULL; wp = wp->w_wndp) { |
220 |
|
|
if (wp->w_bufp == curbp) { |
221 |
|
|
wp->w_dotp = wp->w_linep = bfirstlp(curbp); |
222 |
|
|
wp->w_doto = 0; |
223 |
|
|
wp->w_markp = NULL; |
224 |
|
|
wp->w_marko = 0; |
225 |
|
|
} |
226 |
|
|
} |
227 |
|
|
|
228 |
|
|
/* |
229 |
|
|
* Call auto-executing function if we need to. |
230 |
|
|
*/ |
231 |
|
|
if ((ael = find_autoexec(fname)) != NULL) { |
232 |
|
|
for (i = 0; ael[i] != NULL; i++) |
233 |
|
|
(*ael[i])(0, 1); |
234 |
|
|
free(ael); |
235 |
|
|
} |
236 |
|
|
|
237 |
|
|
/* no change */ |
238 |
|
|
curbp->b_flag &= ~BFCHG; |
239 |
|
|
|
240 |
|
|
/* |
241 |
|
|
* Set the buffer READONLY flag if any of following are true: |
242 |
|
|
* 1. file is a directory. |
243 |
|
|
* 2. file is read-only. |
244 |
|
|
* 3. file doesn't exist and directory is read-only. |
245 |
|
|
*/ |
246 |
|
|
if (fisdir(fname) == TRUE) { |
247 |
|
|
ro = TRUE; |
248 |
|
|
} else if ((access(fname, W_OK) == -1)) { |
249 |
|
|
if (errno != ENOENT) { |
250 |
|
|
ro = TRUE; |
251 |
|
|
} else if (errno == ENOENT) { |
252 |
|
|
(void)xdirname(dp, fname, sizeof(dp)); |
253 |
|
|
(void)strlcat(dp, "/", sizeof(dp)); |
254 |
|
|
|
255 |
|
|
/* Missing directory; keep buffer rw, like emacs */ |
256 |
|
|
if (stat(dp, &statbuf) == -1 && errno == ENOENT) { |
257 |
|
|
if (eyorn("Missing directory, create") == TRUE) |
258 |
|
|
(void)do_makedir(dp); |
259 |
|
|
} else if (access(dp, W_OK) == -1 && errno == EACCES) { |
260 |
|
|
ewprintf("File not found and directory" |
261 |
|
|
" write-protected"); |
262 |
|
|
ro = TRUE; |
263 |
|
|
} |
264 |
|
|
} |
265 |
|
|
} |
266 |
|
|
if (ro == TRUE) |
267 |
|
|
curbp->b_flag |= BFREADONLY; |
268 |
|
|
|
269 |
|
|
if (startrow) { |
270 |
|
|
gotoline(FFARG, startrow); |
271 |
|
|
startrow = 0; |
272 |
|
|
} |
273 |
|
|
|
274 |
|
|
undo_add_modified(); |
275 |
|
|
return (status); |
276 |
|
|
} |
277 |
|
|
|
278 |
|
|
/* |
279 |
|
|
* NB, getting file attributes is done here under control of a flag |
280 |
|
|
* rather than in readin, which would be cleaner. I was concerned |
281 |
|
|
* that some operating system might require the file to be open |
282 |
|
|
* in order to get the information. Similarly for writing. |
283 |
|
|
*/ |
284 |
|
|
|
285 |
|
|
/* |
286 |
|
|
* Insert a file in the current buffer, after dot. If file is a directory, |
287 |
|
|
* and 'replacebuf' is TRUE, invoke dired mode, else die with an error. |
288 |
|
|
* If file is a regular file, set mark at the end of the text inserted; |
289 |
|
|
* point at the beginning. Return a standard status. Print a summary |
290 |
|
|
* (lines read, error message) out as well. This routine also does the |
291 |
|
|
* read end of backup processing. The BFBAK flag, if set in a buffer, |
292 |
|
|
* says that a backup should be taken. It is set when a file is read in, |
293 |
|
|
* but not on a new file. You don't need to make a backup copy of nothing. |
294 |
|
|
*/ |
295 |
|
|
|
296 |
|
|
static char *line = NULL; |
297 |
|
|
static int linesize = 0; |
298 |
|
|
|
299 |
|
|
int |
300 |
|
|
insertfile(char *fname, char *newname, int replacebuf) |
301 |
|
|
{ |
302 |
|
|
struct buffer *bp; |
303 |
|
|
struct line *lp1, *lp2; |
304 |
|
|
struct line *olp; /* line we started at */ |
305 |
|
|
struct mgwin *wp; |
306 |
|
|
int nbytes, s, nline = 0, siz, x, x2; |
307 |
|
|
int opos; /* offset we started at */ |
308 |
|
|
int oline; /* original line number */ |
309 |
|
|
FILE *ffp; |
310 |
|
|
|
311 |
|
|
if (replacebuf == TRUE) |
312 |
|
|
x = undo_enable(FFRAND, 0); |
313 |
|
|
else |
314 |
|
|
x = undo_enabled(); |
315 |
|
|
|
316 |
|
|
lp1 = NULL; |
317 |
|
|
if (line == NULL) { |
318 |
|
|
line = malloc(NLINE); |
319 |
|
|
if (line == NULL) |
320 |
|
|
panic("out of memory"); |
321 |
|
|
linesize = NLINE; |
322 |
|
|
} |
323 |
|
|
|
324 |
|
|
/* cheap */ |
325 |
|
|
bp = curbp; |
326 |
|
|
if (newname != NULL) { |
327 |
|
|
(void)strlcpy(bp->b_fname, newname, sizeof(bp->b_fname)); |
328 |
|
|
(void)xdirname(bp->b_cwd, newname, sizeof(bp->b_cwd)); |
329 |
|
|
(void)strlcat(bp->b_cwd, "/", sizeof(bp->b_cwd)); |
330 |
|
|
} |
331 |
|
|
|
332 |
|
|
/* hard file open */ |
333 |
|
|
if ((s = ffropen(&ffp, fname, (replacebuf == TRUE) ? bp : NULL)) |
334 |
|
|
== FIOERR) |
335 |
|
|
goto out; |
336 |
|
|
if (s == FIOFNF) { |
337 |
|
|
/* file not found */ |
338 |
|
|
if (newname != NULL) |
339 |
|
|
ewprintf("(New file)"); |
340 |
|
|
else |
341 |
|
|
ewprintf("(File not found)"); |
342 |
|
|
goto out; |
343 |
|
|
} else if (s == FIODIR) { |
344 |
|
|
/* file was a directory */ |
345 |
|
|
if (replacebuf == FALSE) { |
346 |
|
|
dobeep(); |
347 |
|
|
ewprintf("Cannot insert: file is a directory, %s", |
348 |
|
|
fname); |
349 |
|
|
goto cleanup; |
350 |
|
|
} |
351 |
|
|
killbuffer(bp); |
352 |
|
|
bp = dired_(fname); |
353 |
|
|
undo_enable(FFRAND, x); |
354 |
|
|
if (bp == NULL) |
355 |
|
|
return (FALSE); |
356 |
|
|
curbp = bp; |
357 |
|
|
return (showbuffer(bp, curwp, WFFULL | WFMODE)); |
358 |
|
|
} else { |
359 |
|
|
(void)xdirname(bp->b_cwd, fname, sizeof(bp->b_cwd)); |
360 |
|
|
(void)strlcat(bp->b_cwd, "/", sizeof(bp->b_cwd)); |
361 |
|
|
} |
362 |
|
|
opos = curwp->w_doto; |
363 |
|
|
oline = curwp->w_dotline; |
364 |
|
|
/* |
365 |
|
|
* Open a new line at dot and start inserting after it. |
366 |
|
|
* We will delete this newline after insertion. |
367 |
|
|
* Disable undo, as we create the undo record manually. |
368 |
|
|
*/ |
369 |
|
|
x2 = undo_enable(FFRAND, 0); |
370 |
|
|
(void)lnewline(); |
371 |
|
|
olp = lback(curwp->w_dotp); |
372 |
|
|
undo_enable(FFRAND, x2); |
373 |
|
|
|
374 |
|
|
nline = 0; |
375 |
|
|
siz = 0; |
376 |
|
|
while ((s = ffgetline(ffp, line, linesize, &nbytes)) != FIOERR) { |
377 |
|
|
retry: |
378 |
|
|
siz += nbytes + 1; |
379 |
|
|
switch (s) { |
380 |
|
|
case FIOSUC: |
381 |
|
|
/* FALLTHRU */ |
382 |
|
|
case FIOEOF: |
383 |
|
|
++nline; |
384 |
|
|
if ((lp1 = lalloc(nbytes)) == NULL) { |
385 |
|
|
/* keep message on the display */ |
386 |
|
|
s = FIOERR; |
387 |
|
|
undo_add_insert(olp, opos, |
388 |
|
|
siz - nbytes - 1 - 1); |
389 |
|
|
goto endoffile; |
390 |
|
|
} |
391 |
|
|
bcopy(line, <ext(lp1)[0], nbytes); |
392 |
|
|
lp2 = lback(curwp->w_dotp); |
393 |
|
|
lp2->l_fp = lp1; |
394 |
|
|
lp1->l_fp = curwp->w_dotp; |
395 |
|
|
lp1->l_bp = lp2; |
396 |
|
|
curwp->w_dotp->l_bp = lp1; |
397 |
|
|
if (s == FIOEOF) { |
398 |
|
|
undo_add_insert(olp, opos, siz - 1); |
399 |
|
|
goto endoffile; |
400 |
|
|
} |
401 |
|
|
break; |
402 |
|
|
case FIOLONG: { |
403 |
|
|
/* a line too long to fit in our buffer */ |
404 |
|
|
char *cp; |
405 |
|
|
int newsize; |
406 |
|
|
|
407 |
|
|
newsize = linesize * 2; |
408 |
|
|
if (newsize < 0 || |
409 |
|
|
(cp = malloc(newsize)) == NULL) { |
410 |
|
|
dobeep(); |
411 |
|
|
ewprintf("Could not allocate %d bytes", |
412 |
|
|
newsize); |
413 |
|
|
s = FIOERR; |
414 |
|
|
goto endoffile; |
415 |
|
|
} |
416 |
|
|
bcopy(line, cp, linesize); |
417 |
|
|
free(line); |
418 |
|
|
line = cp; |
419 |
|
|
s = ffgetline(ffp, line + linesize, linesize, |
420 |
|
|
&nbytes); |
421 |
|
|
nbytes += linesize; |
422 |
|
|
linesize = newsize; |
423 |
|
|
if (s == FIOERR) |
424 |
|
|
goto endoffile; |
425 |
|
|
goto retry; |
426 |
|
|
} |
427 |
|
|
default: |
428 |
|
|
dobeep(); |
429 |
|
|
ewprintf("Unknown code %d reading file", s); |
430 |
|
|
s = FIOERR; |
431 |
|
|
break; |
432 |
|
|
} |
433 |
|
|
} |
434 |
|
|
endoffile: |
435 |
|
|
/* ignore errors */ |
436 |
|
|
(void)ffclose(ffp, NULL); |
437 |
|
|
/* don't zap an error */ |
438 |
|
|
if (s == FIOEOF) { |
439 |
|
|
if (nline == 1) |
440 |
|
|
ewprintf("(Read 1 line)"); |
441 |
|
|
else |
442 |
|
|
ewprintf("(Read %d lines)", nline); |
443 |
|
|
} |
444 |
|
|
/* set mark at the end of the text */ |
445 |
|
|
curwp->w_dotp = curwp->w_markp = lback(curwp->w_dotp); |
446 |
|
|
curwp->w_marko = llength(curwp->w_markp); |
447 |
|
|
curwp->w_markline = oline + nline + 1; |
448 |
|
|
/* |
449 |
|
|
* if we are at the end of the file, ldelnewline is a no-op, |
450 |
|
|
* but we still need to decrement the line and markline counts |
451 |
|
|
* as we've accounted for this fencepost in our arithmetic |
452 |
|
|
*/ |
453 |
|
|
if (lforw(curwp->w_dotp) == curwp->w_bufp->b_headp) { |
454 |
|
|
curwp->w_bufp->b_lines--; |
455 |
|
|
curwp->w_markline--; |
456 |
|
|
} else |
457 |
|
|
(void)ldelnewline(); |
458 |
|
|
curwp->w_dotp = olp; |
459 |
|
|
curwp->w_doto = opos; |
460 |
|
|
curwp->w_dotline = oline; |
461 |
|
|
if (olp == curbp->b_headp) |
462 |
|
|
curwp->w_dotp = lforw(olp); |
463 |
|
|
if (newname != NULL) |
464 |
|
|
bp->b_flag |= BFCHG | BFBAK; /* Need a backup. */ |
465 |
|
|
else |
466 |
|
|
bp->b_flag |= BFCHG; |
467 |
|
|
/* |
468 |
|
|
* If the insert was at the end of buffer, set lp1 to the end of |
469 |
|
|
* buffer line, and lp2 to the beginning of the newly inserted text. |
470 |
|
|
* (Otherwise lp2 is set to NULL.) This is used below to set |
471 |
|
|
* pointers in other windows correctly if they are also at the end of |
472 |
|
|
* buffer. |
473 |
|
|
*/ |
474 |
|
|
lp1 = bp->b_headp; |
475 |
|
|
if (curwp->w_markp == lp1) { |
476 |
|
|
lp2 = curwp->w_dotp; |
477 |
|
|
} else { |
478 |
|
|
/* delete extraneous newline */ |
479 |
|
|
(void)ldelnewline(); |
480 |
|
|
out: lp2 = NULL; |
481 |
|
|
} |
482 |
|
|
for (wp = wheadp; wp != NULL; wp = wp->w_wndp) { |
483 |
|
|
if (wp->w_bufp == curbp) { |
484 |
|
|
wp->w_rflag |= WFMODE | WFEDIT; |
485 |
|
|
if (wp != curwp && lp2 != NULL) { |
486 |
|
|
if (wp->w_dotp == lp1) |
487 |
|
|
wp->w_dotp = lp2; |
488 |
|
|
if (wp->w_markp == lp1) |
489 |
|
|
wp->w_markp = lp2; |
490 |
|
|
if (wp->w_linep == lp1) |
491 |
|
|
wp->w_linep = lp2; |
492 |
|
|
} |
493 |
|
|
} |
494 |
|
|
} |
495 |
|
|
bp->b_lines += nline; |
496 |
|
|
cleanup: |
497 |
|
|
undo_enable(FFRAND, x); |
498 |
|
|
|
499 |
|
|
/* return FALSE if error */ |
500 |
|
|
return (s != FIOERR); |
501 |
|
|
} |
502 |
|
|
|
503 |
|
|
/* |
504 |
|
|
* Ask for a file name and write the contents of the current buffer to that |
505 |
|
|
* file. Update the remembered file name and clear the buffer changed flag. |
506 |
|
|
* This handling of file names is different from the earlier versions and |
507 |
|
|
* is more compatible with Gosling EMACS than with ITS EMACS. |
508 |
|
|
*/ |
509 |
|
|
/* ARGSUSED */ |
510 |
|
|
int |
511 |
|
|
filewrite(int f, int n) |
512 |
|
|
{ |
513 |
|
|
struct stat statbuf; |
514 |
|
|
int s; |
515 |
|
|
char fname[NFILEN], bn[NBUFN], tmp[NFILEN + 25]; |
516 |
|
|
char *adjfname, *bufp; |
517 |
|
|
FILE *ffp; |
518 |
|
|
|
519 |
|
|
if (getbufcwd(fname, sizeof(fname)) != TRUE) |
520 |
|
|
fname[0] = '\0'; |
521 |
|
|
if ((bufp = eread("Write file: ", fname, NFILEN, |
522 |
|
|
EFDEF | EFNEW | EFCR | EFFILE)) == NULL) |
523 |
|
|
return (ABORT); |
524 |
|
|
else if (bufp[0] == '\0') |
525 |
|
|
return (FALSE); |
526 |
|
|
|
527 |
|
|
adjfname = adjustname(fname, TRUE); |
528 |
|
|
if (adjfname == NULL) |
529 |
|
|
return (FALSE); |
530 |
|
|
|
531 |
|
|
/* Check if file exists; write checks done later */ |
532 |
|
|
if (stat(adjfname, &statbuf) == 0) { |
533 |
|
|
if (S_ISDIR(statbuf.st_mode)) { |
534 |
|
|
dobeep(); |
535 |
|
|
ewprintf("%s is a directory", adjfname); |
536 |
|
|
return (FALSE); |
537 |
|
|
} |
538 |
|
|
snprintf(tmp, sizeof(tmp), "File `%s' exists; overwrite", |
539 |
|
|
adjfname); |
540 |
|
|
if ((s = eyorn(tmp)) != TRUE) |
541 |
|
|
return (s); |
542 |
|
|
} |
543 |
|
|
|
544 |
|
|
/* old attributes are no longer current */ |
545 |
|
|
bzero(&curbp->b_fi, sizeof(curbp->b_fi)); |
546 |
|
|
if ((s = writeout(&ffp, curbp, adjfname)) == TRUE) { |
547 |
|
|
(void)strlcpy(curbp->b_fname, adjfname, sizeof(curbp->b_fname)); |
548 |
|
|
if (getbufcwd(curbp->b_cwd, sizeof(curbp->b_cwd)) != TRUE) |
549 |
|
|
(void)strlcpy(curbp->b_cwd, "/", sizeof(curbp->b_cwd)); |
550 |
|
|
if (augbname(bn, curbp->b_fname, sizeof(bn)) |
551 |
|
|
== FALSE) |
552 |
|
|
return (FALSE); |
553 |
|
|
free(curbp->b_bname); |
554 |
|
|
if ((curbp->b_bname = strdup(bn)) == NULL) |
555 |
|
|
return (FALSE); |
556 |
|
|
(void)fupdstat(curbp); |
557 |
|
|
curbp->b_flag &= ~(BFBAK | BFCHG); |
558 |
|
|
upmodes(curbp); |
559 |
|
|
undo_add_boundary(FFRAND, 1); |
560 |
|
|
undo_add_modified(); |
561 |
|
|
} |
562 |
|
|
return (s); |
563 |
|
|
} |
564 |
|
|
|
565 |
|
|
/* |
566 |
|
|
* Save the contents of the current buffer back into its associated file. |
567 |
|
|
*/ |
568 |
|
|
static int makebackup = TRUE; |
569 |
|
|
|
570 |
|
|
/* ARGSUSED */ |
571 |
|
|
int |
572 |
|
|
filesave(int f, int n) |
573 |
|
|
{ |
574 |
|
|
if (curbp->b_fname[0] == '\0') |
575 |
|
|
return (filewrite(f, n)); |
576 |
|
|
else |
577 |
|
|
return (buffsave(curbp)); |
578 |
|
|
} |
579 |
|
|
|
580 |
|
|
/* |
581 |
|
|
* Save the contents of the buffer argument into its associated file. Do |
582 |
|
|
* nothing if there have been no changes (is this a bug, or a feature?). |
583 |
|
|
* Error if there is no remembered file name. If this is the first write |
584 |
|
|
* since the read or visit, then a backup copy of the file is made. |
585 |
|
|
* Allow user to select whether or not to make backup files by looking at |
586 |
|
|
* the value of makebackup. |
587 |
|
|
*/ |
588 |
|
|
int |
589 |
|
|
buffsave(struct buffer *bp) |
590 |
|
|
{ |
591 |
|
|
int s; |
592 |
|
|
FILE *ffp; |
593 |
|
|
|
594 |
|
|
/* return, no changes */ |
595 |
|
|
if ((bp->b_flag & BFCHG) == 0) { |
596 |
|
|
ewprintf("(No changes need to be saved)"); |
597 |
|
|
return (TRUE); |
598 |
|
|
} |
599 |
|
|
|
600 |
|
|
/* must have a name */ |
601 |
|
|
if (bp->b_fname[0] == '\0') { |
602 |
|
|
dobeep(); |
603 |
|
|
ewprintf("No file name"); |
604 |
|
|
return (FALSE); |
605 |
|
|
} |
606 |
|
|
|
607 |
|
|
/* Ensure file has not been modified elsewhere */ |
608 |
|
|
/* We don't use the ignore flag here */ |
609 |
|
|
if (fchecktime(bp) != TRUE) { |
610 |
|
|
if ((s = eyesno("File has changed on disk since last save. " |
611 |
|
|
"Save anyway")) != TRUE) |
612 |
|
|
return (s); |
613 |
|
|
} |
614 |
|
|
|
615 |
|
|
if (makebackup && (bp->b_flag & BFBAK)) { |
616 |
|
|
s = fbackupfile(bp->b_fname); |
617 |
|
|
/* hard error */ |
618 |
|
|
if (s == ABORT) |
619 |
|
|
return (FALSE); |
620 |
|
|
/* softer error */ |
621 |
|
|
if (s == FALSE && |
622 |
|
|
(s = eyesno("Backup error, save anyway")) != TRUE) |
623 |
|
|
return (s); |
624 |
|
|
} |
625 |
|
|
if ((s = writeout(&ffp, bp, bp->b_fname)) == TRUE) { |
626 |
|
|
(void)fupdstat(bp); |
627 |
|
|
bp->b_flag &= ~(BFCHG | BFBAK); |
628 |
|
|
upmodes(bp); |
629 |
|
|
undo_add_boundary(FFRAND, 1); |
630 |
|
|
undo_add_modified(); |
631 |
|
|
} |
632 |
|
|
return (s); |
633 |
|
|
} |
634 |
|
|
|
635 |
|
|
/* |
636 |
|
|
* Since we don't have variables (we probably should) this is a command |
637 |
|
|
* processor for changing the value of the make backup flag. If no argument |
638 |
|
|
* is given, sets makebackup to true, so backups are made. If an argument is |
639 |
|
|
* given, no backup files are made when saving a new version of a file. |
640 |
|
|
*/ |
641 |
|
|
/* ARGSUSED */ |
642 |
|
|
int |
643 |
|
|
makebkfile(int f, int n) |
644 |
|
|
{ |
645 |
|
|
if (f & FFARG) |
646 |
|
|
makebackup = n > 0; |
647 |
|
|
else |
648 |
|
|
makebackup = !makebackup; |
649 |
|
|
ewprintf("Backup files %sabled", makebackup ? "en" : "dis"); |
650 |
|
|
return (TRUE); |
651 |
|
|
} |
652 |
|
|
|
653 |
|
|
/* |
654 |
|
|
* NB: bp is passed to both ffwopen and ffclose because some |
655 |
|
|
* attribute information may need to be updated at open time |
656 |
|
|
* and others after the close. This is OS-dependent. Note |
657 |
|
|
* that the ff routines are assumed to be able to tell whether |
658 |
|
|
* the attribute information has been set up in this buffer |
659 |
|
|
* or not. |
660 |
|
|
*/ |
661 |
|
|
|
662 |
|
|
/* |
663 |
|
|
* This function performs the details of file writing; writing the file |
664 |
|
|
* in buffer bp to file fn. Uses the file management routines in the |
665 |
|
|
* "fileio.c" package. Most of the grief is checking of some sort. |
666 |
|
|
* You may want to call fupdstat() after using this function. |
667 |
|
|
*/ |
668 |
|
|
int |
669 |
|
|
writeout(FILE ** ffp, struct buffer *bp, char *fn) |
670 |
|
|
{ |
671 |
|
|
struct stat statbuf; |
672 |
|
|
int s; |
673 |
|
|
char dp[NFILEN]; |
674 |
|
|
|
675 |
|
|
if (stat(fn, &statbuf) == -1 && errno == ENOENT) { |
676 |
|
|
errno = 0; |
677 |
|
|
(void)xdirname(dp, fn, sizeof(dp)); |
678 |
|
|
(void)strlcat(dp, "/", sizeof(dp)); |
679 |
|
|
if (access(dp, W_OK) && errno == EACCES) { |
680 |
|
|
dobeep(); |
681 |
|
|
ewprintf("Directory %s write-protected", dp); |
682 |
|
|
return (FIOERR); |
683 |
|
|
} else if (errno == ENOENT) { |
684 |
|
|
dobeep(); |
685 |
|
|
ewprintf("%s: no such directory", dp); |
686 |
|
|
return (FIOERR); |
687 |
|
|
} |
688 |
|
|
} |
689 |
|
|
/* open writes message */ |
690 |
|
|
if ((s = ffwopen(ffp, fn, bp)) != FIOSUC) |
691 |
|
|
return (FALSE); |
692 |
|
|
s = ffputbuf(*ffp, bp); |
693 |
|
|
if (s == FIOSUC) { |
694 |
|
|
/* no write error */ |
695 |
|
|
s = ffclose(*ffp, bp); |
696 |
|
|
if (s == FIOSUC) |
697 |
|
|
ewprintf("Wrote %s", fn); |
698 |
|
|
} else { |
699 |
|
|
/* print a message indicating write error */ |
700 |
|
|
(void)ffclose(*ffp, bp); |
701 |
|
|
dobeep(); |
702 |
|
|
ewprintf("Unable to write %s", fn); |
703 |
|
|
} |
704 |
|
|
return (s == FIOSUC); |
705 |
|
|
} |
706 |
|
|
|
707 |
|
|
/* |
708 |
|
|
* Tag all windows for bp (all windows if bp == NULL) as needing their |
709 |
|
|
* mode line updated. |
710 |
|
|
*/ |
711 |
|
|
void |
712 |
|
|
upmodes(struct buffer *bp) |
713 |
|
|
{ |
714 |
|
|
struct mgwin *wp; |
715 |
|
|
|
716 |
|
|
for (wp = wheadp; wp != NULL; wp = wp->w_wndp) |
717 |
|
|
if (bp == NULL || curwp->w_bufp == bp) |
718 |
|
|
wp->w_rflag |= WFMODE; |
719 |
|
|
} |
720 |
|
|
|
721 |
|
|
/* |
722 |
|
|
* dirname using strlcpy semantic. |
723 |
|
|
* Like dirname() except an empty string is returned in |
724 |
|
|
* place of "/". This means we can always add a trailing |
725 |
|
|
* slash and be correct. |
726 |
|
|
* Address portability issues by copying argument |
727 |
|
|
* before using. Some implementations modify the input string. |
728 |
|
|
*/ |
729 |
|
|
size_t |
730 |
|
|
xdirname(char *dp, const char *path, size_t dplen) |
731 |
|
|
{ |
732 |
|
|
char ts[NFILEN]; |
733 |
|
|
size_t len; |
734 |
|
|
|
735 |
|
|
(void)strlcpy(ts, path, NFILEN); |
736 |
|
|
len = strlcpy(dp, dirname(ts), dplen); |
737 |
|
|
if (dplen > 0 && dp[0] == '/' && dp[1] == '\0') { |
738 |
|
|
dp[0] = '\0'; |
739 |
|
|
len = 0; |
740 |
|
|
} |
741 |
|
|
return (len); |
742 |
|
|
} |
743 |
|
|
|
744 |
|
|
/* |
745 |
|
|
* basename using strlcpy/strlcat semantic. |
746 |
|
|
* Address portability issue by copying argument |
747 |
|
|
* before using: some implementations modify the input string. |
748 |
|
|
*/ |
749 |
|
|
size_t |
750 |
|
|
xbasename(char *bp, const char *path, size_t bplen) |
751 |
|
|
{ |
752 |
|
|
char ts[NFILEN]; |
753 |
|
|
|
754 |
|
|
(void)strlcpy(ts, path, NFILEN); |
755 |
|
|
return (strlcpy(bp, basename(ts), bplen)); |
756 |
|
|
} |
757 |
|
|
|
758 |
|
|
/* |
759 |
|
|
* The adjusted file name refers to a directory, so open dired mode. |
760 |
|
|
*/ |
761 |
|
|
int |
762 |
|
|
do_dired(char *adjf) |
763 |
|
|
{ |
764 |
|
|
struct buffer *bp; |
765 |
|
|
|
766 |
|
|
if ((bp = dired_(adjf)) == FALSE) |
767 |
|
|
return (FALSE); |
768 |
|
|
curbp = bp; |
769 |
|
|
return (showbuffer(bp, curwp, WFFULL | WFMODE)); |
770 |
|
|
} |