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