File: | src/usr.bin/mg/file.c |
Warning: | line 698, column 7 Although the value stored to 's' is used in the enclosing expression, the value is never actually read from 's' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* $OpenBSD: file.c,v 1.102 2019/06/22 15:03:43 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(*__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[NFILEN1024], *bufp, *adjf; |
32 | |
33 | if (getbufcwd(fname, sizeof(fname)) != TRUE1) |
34 | fname[0] = '\0'; |
35 | bufp = eread("Insert file: ", fname, NFILEN1024, |
36 | EFNEW0x0008 | EFCR0x0010 | EFFILE0x0004 | EFDEF0x0020); |
37 | if (bufp == NULL((void *)0)) |
38 | return (ABORT2); |
39 | else if (bufp[0] == '\0') |
40 | return (FALSE0); |
41 | adjf = adjustname(bufp, TRUE1); |
42 | if (adjf == NULL((void *)0)) |
43 | return (FALSE0); |
44 | return (insertfile(adjf, NULL((void *)0), FALSE0)); |
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[NFILEN1024], *bufp, *adjf; |
59 | int status; |
60 | |
61 | if (getbufcwd(fname, sizeof(fname)) != TRUE1) |
62 | fname[0] = '\0'; |
63 | bufp = eread("Find file: ", fname, NFILEN1024, |
64 | EFNEW0x0008 | EFCR0x0010 | EFFILE0x0004 | EFDEF0x0020); |
65 | if (bufp == NULL((void *)0)) |
66 | return (ABORT2); |
67 | else if (bufp[0] == '\0') |
68 | return (FALSE0); |
69 | adjf = adjustname(fname, TRUE1); |
70 | if (adjf == NULL((void *)0)) |
71 | return (FALSE0); |
72 | if (fisdir(adjf) == TRUE1) |
73 | return (do_dired(adjf)); |
74 | if ((bp = findbuffer(adjf)) == NULL((void *)0)) |
75 | return (FALSE0); |
76 | curbp = bp; |
77 | if (showbuffer(bp, curwp, WFFULL0x08) != TRUE1) |
78 | return (FALSE0); |
79 | if (bp->b_fname[0] == '\0') { |
80 | if ((status = readin(adjf)) != TRUE1) |
81 | killbuffer(bp); |
82 | return (status); |
83 | } |
84 | return (TRUE1); |
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[NFILEN1024], *bufp; |
98 | |
99 | if (getbufcwd(fname, sizeof(fname)) != TRUE1) |
100 | fname[0] = '\0'; |
101 | bufp = eread("Find alternate file: ", fname, NFILEN1024, |
102 | EFNEW0x0008 | EFCR0x0010 | EFFILE0x0004 | EFDEF0x0020); |
103 | if (bufp == NULL((void *)0)) |
104 | return (ABORT2); |
105 | else if (bufp[0] == '\0') |
106 | return (FALSE0); |
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 == ABORT2 || status == FALSE0) |
120 | return (ABORT2); |
121 | |
122 | adjf = adjustname(fn, TRUE1); |
123 | if (adjf == NULL((void *)0)) |
124 | return (FALSE0); |
125 | if (fisdir(adjf) == TRUE1) |
126 | return (do_dired(adjf)); |
127 | if ((bp = findbuffer(adjf)) == NULL((void *)0)) |
128 | return (FALSE0); |
129 | curbp = bp; |
130 | if (showbuffer(bp, curwp, WFFULL0x08) != TRUE1) |
131 | return (FALSE0); |
132 | if (bp->b_fname[0] == '\0') { |
133 | if ((status = readin(adjf)) != TRUE1) |
134 | killbuffer(bp); |
135 | return (status); |
136 | } |
137 | return (TRUE1); |
138 | } |
139 | |
140 | int |
141 | filevisitro(int f, int n) |
142 | { |
143 | int error; |
144 | |
145 | error = filevisit(f, n); |
146 | if (error != TRUE1) |
147 | return (error); |
148 | curbp->b_flag |= BFREADONLY0x10; |
149 | return (TRUE1); |
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[NFILEN1024], *adjf, *bufp; |
163 | int status; |
164 | |
165 | if (getbufcwd(fname, sizeof(fname)) != TRUE1) |
166 | fname[0] = '\0'; |
167 | if ((bufp = eread("Find file in other window: ", fname, NFILEN1024, |
168 | EFNEW0x0008 | EFCR0x0010 | EFFILE0x0004 | EFDEF0x0020)) == NULL((void *)0)) |
169 | return (ABORT2); |
170 | else if (bufp[0] == '\0') |
171 | return (FALSE0); |
172 | adjf = adjustname(fname, TRUE1); |
173 | if (adjf == NULL((void *)0)) |
174 | return (FALSE0); |
175 | if (fisdir(adjf) == TRUE1) |
176 | return (do_dired(adjf)); |
177 | if ((bp = findbuffer(adjf)) == NULL((void *)0)) |
178 | return (FALSE0); |
179 | if (bp == curbp) |
180 | return (splitwind(f, n)); |
181 | if ((wp = popbuf(bp, WNONE0x00)) == NULL((void *)0)) |
182 | return (FALSE0); |
183 | curbp = bp; |
184 | curwp = wp; |
185 | if (bp->b_fname[0] == '\0') { |
186 | if ((status = readin(adjf)) != TRUE1) |
187 | killbuffer(bp); |
188 | return (status); |
189 | } |
190 | return (TRUE1); |
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 = FALSE0; |
205 | PF *ael; |
206 | char dp[NFILEN1024]; |
207 | |
208 | /* might be old */ |
209 | if (bclear(curbp) != TRUE1) |
210 | return (TRUE1); |
211 | /* Clear readonly. May be set by autoexec path */ |
212 | curbp->b_flag &= ~BFREADONLY0x10; |
213 | if ((status = insertfile(fname, fname, TRUE1)) != TRUE1) { |
214 | dobeep(); |
215 | ewprintf("File is not readable: %s", fname); |
216 | return (FALSE0); |
217 | } |
218 | |
219 | for (wp = wheadp; wp != NULL((void *)0); wp = wp->w_wndpw_list.l_p.l_wp) { |
220 | if (wp->w_bufp == curbp) { |
221 | wp->w_dotp = wp->w_linep = bfirstlp(curbp)((((curbp)->b_headp)->l_fp)); |
222 | wp->w_doto = 0; |
223 | wp->w_markp = NULL((void *)0); |
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((void *)0)) { |
232 | for (i = 0; ael[i] != NULL((void *)0); i++) |
233 | (*ael[i])(0, 1); |
234 | free(ael); |
235 | } |
236 | |
237 | /* no change */ |
238 | curbp->b_flag &= ~BFCHG0x01; |
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) == TRUE1) { |
247 | ro = TRUE1; |
248 | } else if ((access(fname, W_OK0x02) == -1)) { |
249 | if (errno(*__errno()) != ENOENT2) { |
250 | ro = TRUE1; |
251 | } else if (errno(*__errno()) == ENOENT2) { |
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(*__errno()) == ENOENT2) { |
257 | if (eyorn("Missing directory, create") == TRUE1) |
258 | (void)do_makedir(dp); |
259 | } else if (access(dp, W_OK0x02) == -1 && errno(*__errno()) == EACCES13) { |
260 | ewprintf("File not found and directory" |
261 | " write-protected"); |
262 | ro = TRUE1; |
263 | } |
264 | } |
265 | } |
266 | if (ro == TRUE1) |
267 | curbp->b_flag |= BFREADONLY0x10; |
268 | |
269 | if (startrow) { |
270 | gotoline(FFARG7, 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((void *)0); |
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 == TRUE1) |
312 | x = undo_enable(FFRAND8, 0); |
313 | else |
314 | x = undo_enabled(); |
315 | |
316 | lp1 = NULL((void *)0); |
317 | if (line == NULL((void *)0)) { |
318 | line = malloc(NLINE256); |
319 | if (line == NULL((void *)0)) |
320 | panic("out of memory"); |
321 | linesize = NLINE256; |
322 | } |
323 | |
324 | /* cheap */ |
325 | bp = curbp; |
326 | if (newname != NULL((void *)0)) { |
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 == TRUE1) ? bp : NULL((void *)0))) |
334 | == FIOERR3) |
335 | goto out; |
336 | if (s == FIOFNF1) { |
337 | /* file not found */ |
338 | if (newname != NULL((void *)0)) |
339 | ewprintf("(New file)"); |
340 | else |
341 | ewprintf("(File not found)"); |
342 | goto out; |
343 | } else if (s == FIODIR5) { |
344 | /* file was a directory */ |
345 | if (replacebuf == FALSE0) { |
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(FFRAND8, x); |
354 | if (bp == NULL((void *)0)) |
355 | return (FALSE0); |
356 | curbp = bp; |
357 | return (showbuffer(bp, curwp, WFFULL0x08 | WFMODE0x10)); |
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(FFRAND8, 0); |
370 | (void)lnewline(); |
371 | olp = lback(curwp->w_dotp)((curwp->w_dotp)->l_bp); |
372 | undo_enable(FFRAND8, x2); |
373 | |
374 | nline = 0; |
375 | siz = 0; |
376 | while ((s = ffgetline(ffp, line, linesize, &nbytes)) != FIOERR3) { |
377 | retry: |
378 | siz += nbytes + 1; |
379 | switch (s) { |
380 | case FIOSUC0: |
381 | /* FALLTHRU */ |
382 | case FIOEOF2: |
383 | ++nline; |
384 | if ((lp1 = lalloc(nbytes)) == NULL((void *)0)) { |
385 | /* keep message on the display */ |
386 | s = FIOERR3; |
387 | undo_add_insert(olp, opos, |
388 | siz - nbytes - 1 - 1); |
389 | goto endoffile; |
390 | } |
391 | bcopy(line, <ext(lp1)((lp1)->l_text)[0], nbytes); |
392 | lp2 = lback(curwp->w_dotp)((curwp->w_dotp)->l_bp); |
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 == FIOEOF2) { |
398 | undo_add_insert(olp, opos, siz - 1); |
399 | goto endoffile; |
400 | } |
401 | break; |
402 | case FIOLONG4: { |
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((void *)0)) { |
410 | dobeep(); |
411 | ewprintf("Could not allocate %d bytes", |
412 | newsize); |
413 | s = FIOERR3; |
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 == FIOERR3) |
424 | goto endoffile; |
425 | goto retry; |
426 | } |
427 | default: |
428 | dobeep(); |
429 | ewprintf("Unknown code %d reading file", s); |
430 | s = FIOERR3; |
431 | break; |
432 | } |
433 | } |
434 | endoffile: |
435 | /* ignore errors */ |
436 | (void)ffclose(ffp, NULL((void *)0)); |
437 | /* don't zap an error */ |
438 | if (s == FIOEOF2) { |
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)((curwp->w_dotp)->l_bp); |
446 | curwp->w_marko = llength(curwp->w_markp)((curwp->w_markp)->l_used); |
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_dotp)->l_fp) == 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)((olp)->l_fp); |
463 | if (newname != NULL((void *)0)) |
464 | bp->b_flag |= BFCHG0x01 | BFBAK0x02; /* Need a backup. */ |
465 | else |
466 | bp->b_flag |= BFCHG0x01; |
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((void *)0); |
481 | } |
482 | for (wp = wheadp; wp != NULL((void *)0); wp = wp->w_wndpw_list.l_p.l_wp) { |
483 | if (wp->w_bufp == curbp) { |
484 | wp->w_rflag |= WFMODE0x10 | WFEDIT0x04; |
485 | if (wp != curwp && lp2 != NULL((void *)0)) { |
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(FFRAND8, x); |
498 | |
499 | /* return FALSE if error */ |
500 | return (s != FIOERR3); |
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[NFILEN1024], bn[NBUFN1024], tmp[NFILEN1024 + 25]; |
516 | char *adjfname, *bufp; |
517 | FILE *ffp; |
518 | |
519 | if (getbufcwd(fname, sizeof(fname)) != TRUE1) |
520 | fname[0] = '\0'; |
521 | if ((bufp = eread("Write file: ", fname, NFILEN1024, |
522 | EFDEF0x0020 | EFNEW0x0008 | EFCR0x0010 | EFFILE0x0004)) == NULL((void *)0)) |
523 | return (ABORT2); |
524 | else if (bufp[0] == '\0') |
525 | return (FALSE0); |
526 | |
527 | adjfname = adjustname(fname, TRUE1); |
528 | if (adjfname == NULL((void *)0)) |
529 | return (FALSE0); |
530 | |
531 | /* Check if file exists; write checks done later */ |
532 | if (stat(adjfname, &statbuf) == 0) { |
533 | if (S_ISDIR(statbuf.st_mode)((statbuf.st_mode & 0170000) == 0040000)) { |
534 | dobeep(); |
535 | ewprintf("%s is a directory", adjfname); |
536 | return (FALSE0); |
537 | } |
538 | snprintf(tmp, sizeof(tmp), "File `%s' exists; overwrite", |
539 | adjfname); |
540 | if ((s = eyorn(tmp)) != TRUE1) |
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)) == TRUE1) { |
547 | (void)strlcpy(curbp->b_fname, adjfname, sizeof(curbp->b_fname)); |
548 | if (getbufcwd(curbp->b_cwd, sizeof(curbp->b_cwd)) != TRUE1) |
549 | (void)strlcpy(curbp->b_cwd, "/", sizeof(curbp->b_cwd)); |
550 | if (augbname(bn, curbp->b_fname, sizeof(bn)) |
551 | == FALSE0) |
552 | return (FALSE0); |
553 | free(curbp->b_bnameb_list.l_name); |
554 | if ((curbp->b_bnameb_list.l_name = strdup(bn)) == NULL((void *)0)) |
555 | return (FALSE0); |
556 | (void)fupdstat(curbp); |
557 | curbp->b_flag &= ~(BFBAK0x02 | BFCHG0x01); |
558 | upmodes(curbp); |
559 | undo_add_boundary(FFRAND8, 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 = TRUE1; |
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 & BFCHG0x01) == 0) { |
596 | ewprintf("(No changes need to be saved)"); |
597 | return (TRUE1); |
598 | } |
599 | |
600 | /* must have a name */ |
601 | if (bp->b_fname[0] == '\0') { |
602 | dobeep(); |
603 | ewprintf("No file name"); |
604 | return (FALSE0); |
605 | } |
606 | |
607 | /* Ensure file has not been modified elsewhere */ |
608 | /* We don't use the ignore flag here */ |
609 | if (fchecktime(bp) != TRUE1) { |
610 | if ((s = eyesno("File has changed on disk since last save. " |
611 | "Save anyway")) != TRUE1) |
612 | return (s); |
613 | } |
614 | |
615 | if (makebackup && (bp->b_flag & BFBAK0x02)) { |
616 | s = fbackupfile(bp->b_fname); |
617 | /* hard error */ |
618 | if (s == ABORT2) |
619 | return (FALSE0); |
620 | /* softer error */ |
621 | if (s == FALSE0 && |
622 | (s = eyesno("Backup error, save anyway")) != TRUE1) |
623 | return (s); |
624 | } |
625 | if ((s = writeout(&ffp, bp, bp->b_fname)) == TRUE1) { |
626 | (void)fupdstat(bp); |
627 | bp->b_flag &= ~(BFCHG0x01 | BFBAK0x02); |
628 | upmodes(bp); |
629 | undo_add_boundary(FFRAND8, 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 & FFARG7) |
646 | makebackup = n > 0; |
647 | else |
648 | makebackup = !makebackup; |
649 | ewprintf("Backup files %sabled", makebackup ? "en" : "dis"); |
650 | return (TRUE1); |
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 | struct line *lpend; |
673 | int s, eobnl; |
674 | char dp[NFILEN1024]; |
675 | |
676 | if (stat(fn, &statbuf) == -1 && errno(*__errno()) == ENOENT2) { |
677 | errno(*__errno()) = 0; |
678 | (void)xdirname(dp, fn, sizeof(dp)); |
679 | (void)strlcat(dp, "/", sizeof(dp)); |
680 | if (access(dp, W_OK0x02) && errno(*__errno()) == EACCES13) { |
681 | dobeep(); |
682 | ewprintf("Directory %s write-protected", dp); |
683 | return (FIOERR3); |
684 | } else if (errno(*__errno()) == ENOENT2) { |
685 | dobeep(); |
686 | ewprintf("%s: no such directory", dp); |
687 | return (FIOERR3); |
688 | } |
689 | } |
690 | lpend = bp->b_headp; |
691 | eobnl = 0; |
692 | if (llength(lback(lpend))((((lpend)->l_bp))->l_used) != 0) { |
693 | eobnl = eyorn("No newline at end of file, add one"); |
694 | if (eobnl != TRUE1 && eobnl != FALSE0) |
695 | return (eobnl); /* abort */ |
696 | } |
697 | /* open writes message */ |
698 | if ((s = ffwopen(ffp, fn, bp)) != FIOSUC0) |
Although the value stored to 's' is used in the enclosing expression, the value is never actually read from 's' | |
699 | return (FALSE0); |
700 | s = ffputbuf(*ffp, bp, eobnl); |
701 | if (s == FIOSUC0) { |
702 | /* no write error */ |
703 | s = ffclose(*ffp, bp); |
704 | if (s == FIOSUC0) |
705 | ewprintf("Wrote %s", fn); |
706 | } else { |
707 | /* print a message indicating write error */ |
708 | (void)ffclose(*ffp, bp); |
709 | dobeep(); |
710 | ewprintf("Unable to write %s", fn); |
711 | } |
712 | return (s == FIOSUC0); |
713 | } |
714 | |
715 | /* |
716 | * Tag all windows for bp (all windows if bp == NULL) as needing their |
717 | * mode line updated. |
718 | */ |
719 | void |
720 | upmodes(struct buffer *bp) |
721 | { |
722 | struct mgwin *wp; |
723 | |
724 | for (wp = wheadp; wp != NULL((void *)0); wp = wp->w_wndpw_list.l_p.l_wp) |
725 | if (bp == NULL((void *)0) || curwp->w_bufp == bp) |
726 | wp->w_rflag |= WFMODE0x10; |
727 | } |
728 | |
729 | /* |
730 | * dirname using strlcpy semantic. |
731 | * Like dirname() except an empty string is returned in |
732 | * place of "/". This means we can always add a trailing |
733 | * slash and be correct. |
734 | * Address portability issues by copying argument |
735 | * before using. Some implementations modify the input string. |
736 | */ |
737 | size_t |
738 | xdirname(char *dp, const char *path, size_t dplen) |
739 | { |
740 | char ts[NFILEN1024]; |
741 | size_t len; |
742 | |
743 | (void)strlcpy(ts, path, NFILEN1024); |
744 | len = strlcpy(dp, dirname(ts), dplen); |
745 | if (dplen > 0 && dp[0] == '/' && dp[1] == '\0') { |
746 | dp[0] = '\0'; |
747 | len = 0; |
748 | } |
749 | return (len); |
750 | } |
751 | |
752 | /* |
753 | * basename using strlcpy/strlcat semantic. |
754 | * Address portability issue by copying argument |
755 | * before using: some implementations modify the input string. |
756 | */ |
757 | size_t |
758 | xbasename(char *bp, const char *path, size_t bplen) |
759 | { |
760 | char ts[NFILEN1024]; |
761 | |
762 | (void)strlcpy(ts, path, NFILEN1024); |
763 | return (strlcpy(bp, basename(ts), bplen)); |
764 | } |
765 | |
766 | /* |
767 | * The adjusted file name refers to a directory, so open dired mode. |
768 | */ |
769 | int |
770 | do_dired(char *adjf) |
771 | { |
772 | struct buffer *bp; |
773 | |
774 | if ((bp = dired_(adjf)) == FALSE0) |
775 | return (FALSE0); |
776 | curbp = bp; |
777 | return (showbuffer(bp, curwp, WFFULL0x08 | WFMODE0x10)); |
778 | } |