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