| 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 | } |