1 /* $OpenBSD: file.c,v 1.71 2009/06/04 23:39:37 kjell Exp $ */ 2 3 /* This file is in the public domain. */ 4 5 /* 6 * File commands. 7 */ 8 9 #include <libgen.h> 10 11 #include "def.h" 12 13 static char *xdirname(const char *); 14 15 /* 16 * Insert a file into the current buffer. Real easy - just call the 17 * insertfile routine with the file name. 18 */ 19 /* ARGSUSED */ 20 int 21 fileinsert(int f, int n) 22 { 23 char fname[NFILEN], *bufp, *adjf; 24 25 if (getbufcwd(fname, sizeof(fname)) != TRUE) 26 fname[0] = '\0'; 27 bufp = eread("Insert file: ", fname, NFILEN, 28 EFNEW | EFCR | EFFILE | EFDEF); 29 if (bufp == NULL) 30 return (ABORT); 31 else if (bufp[0] == '\0') 32 return (FALSE); 33 adjf = adjustname(bufp, TRUE); 34 if (adjf == NULL) 35 return (FALSE); 36 return (insertfile(adjf, NULL, FALSE)); 37 } 38 39 /* 40 * Select a file for editing. Look around to see if you can find the file 41 * in another buffer; if you can find it, just switch to the buffer. If 42 * you cannot find the file, create a new buffer, read in the text, and 43 * switch to the new buffer. 44 */ 45 /* ARGSUSED */ 46 int 47 filevisit(int f, int n) 48 { 49 struct buffer *bp; 50 char fname[NFILEN], *bufp, *adjf; 51 int status; 52 53 if (getbufcwd(fname, sizeof(fname)) != TRUE) 54 fname[0] = '\0'; 55 bufp = eread("Find file: ", fname, NFILEN, 56 EFNEW | EFCR | EFFILE | EFDEF); 57 if (bufp == NULL) 58 return (ABORT); 59 else if (bufp[0] == '\0') 60 return (FALSE); 61 adjf = adjustname(fname, TRUE); 62 if (adjf == NULL) 63 return (FALSE); 64 if ((bp = findbuffer(adjf)) == NULL) 65 return (FALSE); 66 curbp = bp; 67 if (showbuffer(bp, curwp, WFFULL) != TRUE) 68 return (FALSE); 69 if (bp->b_fname[0] == '\0') { 70 if ((status = readin(adjf)) != TRUE) 71 killbuffer(bp); 72 return (status); 73 } 74 return (TRUE); 75 } 76 77 /* 78 * Replace the current file with an alternate one. Semantics for finding 79 * the replacement file are the same as 'filevisit', except the current 80 * buffer is killed before the switch. If the kill fails, or is aborted, 81 * revert to the original file. 82 */ 83 /* ARGSUSED */ 84 int 85 filevisitalt(int f, int n) 86 { 87 struct buffer *bp; 88 char fname[NFILEN], *bufp, *adjf; 89 int status; 90 91 if (getbufcwd(fname, sizeof(fname)) != TRUE) 92 fname[0] = '\0'; 93 bufp = eread("Find alternate file: ", fname, NFILEN, 94 EFNEW | EFCR | EFFILE | EFDEF); 95 if (bufp == NULL) 96 return (ABORT); 97 else if (bufp[0] == '\0') 98 return (FALSE); 99 100 status = killbuffer(curbp); 101 if (status == ABORT || status == FALSE) 102 return (ABORT); 103 104 adjf = adjustname(fname, TRUE); 105 if (adjf == NULL) 106 return (FALSE); 107 if ((bp = findbuffer(adjf)) == NULL) 108 return (FALSE); 109 curbp = bp; 110 if (showbuffer(bp, curwp, WFFULL) != TRUE) 111 return (FALSE); 112 if (bp->b_fname[0] == '\0') { 113 if ((status = readin(adjf)) != TRUE) 114 killbuffer(bp); 115 return (status); 116 } 117 return (TRUE); 118 } 119 120 int 121 filevisitro(int f, int n) 122 { 123 int error; 124 125 error = filevisit(f, n); 126 if (error != TRUE) 127 return (error); 128 curbp->b_flag |= BFREADONLY; 129 return (TRUE); 130 } 131 132 /* 133 * Pop to a file in the other window. Same as the last function, but uses 134 * popbuf instead of showbuffer. 135 */ 136 /* ARGSUSED */ 137 int 138 poptofile(int f, int n) 139 { 140 struct buffer *bp; 141 struct mgwin *wp; 142 char fname[NFILEN], *adjf, *bufp; 143 int status; 144 145 if (getbufcwd(fname, sizeof(fname)) != TRUE) 146 fname[0] = '\0'; 147 if ((bufp = eread("Find file in other window: ", fname, NFILEN, 148 EFNEW | EFCR | EFFILE | EFDEF)) == NULL) 149 return (ABORT); 150 else if (bufp[0] == '\0') 151 return (FALSE); 152 adjf = adjustname(fname, TRUE); 153 if (adjf == NULL) 154 return (FALSE); 155 if ((bp = findbuffer(adjf)) == NULL) 156 return (FALSE); 157 if (bp == curbp) 158 return (splitwind(f, n)); 159 if ((wp = popbuf(bp, WNONE)) == NULL) 160 return (FALSE); 161 curbp = bp; 162 curwp = wp; 163 if (bp->b_fname[0] == '\0') { 164 if ((status = readin(adjf)) != TRUE) 165 killbuffer(bp); 166 return (status); 167 } 168 return (TRUE); 169 } 170 171 /* 172 * Given a file name, either find the buffer it uses, or create a new 173 * empty buffer to put it in. 174 */ 175 struct buffer * 176 findbuffer(char *fn) 177 { 178 struct buffer *bp; 179 char bname[NBUFN], fname[NBUFN]; 180 181 if (strlcpy(fname, fn, sizeof(fname)) >= sizeof(fname)) { 182 ewprintf("filename too long"); 183 return (NULL); 184 } 185 186 for (bp = bheadp; bp != NULL; bp = bp->b_bufp) { 187 if (strcmp(bp->b_fname, fname) == 0) 188 return (bp); 189 } 190 /* Not found. Create a new one, adjusting name first */ 191 if (augbname(bname, fname, sizeof(bname)) == FALSE) 192 return (NULL); 193 194 bp = bfind(bname, TRUE); 195 return (bp); 196 } 197 198 /* 199 * Read the file "fname" into the current buffer. Make all of the text 200 * in the buffer go away, after checking for unsaved changes. This is 201 * called by the "read" command, the "visit" command, and the mainline 202 * (for "mg file"). 203 */ 204 int 205 readin(char *fname) 206 { 207 struct mgwin *wp; 208 int status, i, ro = FALSE; 209 PF *ael; 210 211 /* might be old */ 212 if (bclear(curbp) != TRUE) 213 return (TRUE); 214 /* Clear readonly. May be set by autoexec path */ 215 curbp->b_flag &= ~BFREADONLY; 216 if ((status = insertfile(fname, fname, TRUE)) != TRUE) { 217 ewprintf("File is not readable: %s", fname); 218 return (FALSE); 219 } 220 221 for (wp = wheadp; wp != NULL; wp = wp->w_wndp) { 222 if (wp->w_bufp == curbp) { 223 wp->w_dotp = wp->w_linep = bfirstlp(curbp); 224 wp->w_doto = 0; 225 wp->w_markp = NULL; 226 wp->w_marko = 0; 227 } 228 } 229 230 /* 231 * Call auto-executing function if we need to. 232 */ 233 if ((ael = find_autoexec(fname)) != NULL) { 234 for (i = 0; ael[i] != NULL; i++) 235 (*ael[i])(0, 1); 236 free(ael); 237 } 238 239 /* no change */ 240 curbp->b_flag &= ~BFCHG; 241 242 /* 243 * We need to set the READONLY flag after we insert the file, 244 * unless the file is a directory. 245 */ 246 if (access(fname, W_OK) && errno != ENOENT) 247 ro = TRUE; 248 if (fisdir(fname) == TRUE) 249 ro = TRUE; 250 if (ro == TRUE) 251 curbp->b_flag |= BFREADONLY; 252 253 if (startrow) { 254 gotoline(FFARG, startrow); 255 startrow = 0; 256 } 257 258 undo_add_modified(); 259 return (status); 260 } 261 262 /* 263 * NB, getting file attributes is done here under control of a flag 264 * rather than in readin, which would be cleaner. I was concerned 265 * that some operating system might require the file to be open 266 * in order to get the information. Similarly for writing. 267 */ 268 269 /* 270 * Insert a file in the current buffer, after dot. If file is a directory, 271 * and 'replacebuf' is TRUE, invoke dired mode, else die with an error. 272 * If file is a regular file, set mark at the end of the text inserted; 273 * point at the beginning. Return a standard status. Print a summary 274 * (lines read, error message) out as well. This routine also does the 275 * read end of backup processing. The BFBAK flag, if set in a buffer, 276 * says that a backup should be taken. It is set when a file is read in, 277 * but not on a new file. You don't need to make a backup copy of nothing. 278 */ 279 280 static char *line = NULL; 281 static int linesize = 0; 282 283 int 284 insertfile(char *fname, char *newname, int replacebuf) 285 { 286 struct buffer *bp; 287 struct line *lp1, *lp2; 288 struct line *olp; /* line we started at */ 289 struct mgwin *wp; 290 int nbytes, s, nline = 0, siz, x, x2; 291 int opos; /* offset we started at */ 292 int oline; /* original line number */ 293 char *dp; 294 295 if (replacebuf == TRUE) 296 x = undo_enable(FFRAND, 0); 297 else 298 x = undo_enabled(); 299 300 lp1 = NULL; 301 if (line == NULL) { 302 line = malloc(NLINE); 303 if (line == NULL) 304 panic("out of memory"); 305 linesize = NLINE; 306 } 307 308 /* cheap */ 309 bp = curbp; 310 if (newname != NULL) { 311 (void)strlcpy(bp->b_fname, newname, sizeof(bp->b_fname)); 312 dp = xdirname(newname); 313 (void)strlcpy(bp->b_cwd, dp, sizeof(bp->b_cwd)); 314 (void)strlcat(bp->b_cwd, "/", sizeof(bp->b_cwd)); 315 free(dp); 316 } 317 318 /* hard file open */ 319 if ((s = ffropen(fname, (replacebuf == TRUE) ? bp : NULL)) == FIOERR) 320 goto out; 321 if (s == FIOFNF) { 322 /* file not found */ 323 if (newname != NULL) 324 ewprintf("(New file)"); 325 else 326 ewprintf("(File not found)"); 327 goto out; 328 } else if (s == FIODIR) { 329 /* file was a directory */ 330 if (replacebuf == FALSE) { 331 ewprintf("Cannot insert: file is a directory, %s", 332 fname); 333 goto cleanup; 334 } 335 killbuffer(bp); 336 if ((bp = dired_(fname)) == NULL) 337 return (FALSE); 338 undo_enable(FFRAND, x); 339 curbp = bp; 340 return (showbuffer(bp, curwp, WFFULL | WFMODE)); 341 } else { 342 dp = xdirname(fname); 343 (void)strlcpy(bp->b_cwd, dp, sizeof(bp->b_cwd)); 344 (void)strlcat(bp->b_cwd, "/", sizeof(bp->b_cwd)); 345 free(dp); 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(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(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 ffclose(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 int s; 497 char fname[NFILEN], bn[NBUFN]; 498 char *adjfname, *bufp; 499 500 if (getbufcwd(fname, sizeof(fname)) != TRUE) 501 fname[0] = '\0'; 502 if ((bufp = eread("Write file: ", fname, NFILEN, 503 EFDEF | EFNEW | EFCR | EFFILE)) == NULL) 504 return (ABORT); 505 else if (bufp[0] == '\0') 506 return (FALSE); 507 508 adjfname = adjustname(fname, TRUE); 509 if (adjfname == NULL) 510 return (FALSE); 511 /* old attributes are no longer current */ 512 bzero(&curbp->b_fi, sizeof(curbp->b_fi)); 513 if ((s = writeout(curbp, adjfname)) == TRUE) { 514 (void)strlcpy(curbp->b_fname, adjfname, sizeof(curbp->b_fname)); 515 if (getbufcwd(curbp->b_cwd, sizeof(curbp->b_cwd)) != TRUE) 516 (void)strlcpy(curbp->b_cwd, "/", sizeof(curbp->b_cwd)); 517 if (augbname(bn, basename(curbp->b_fname), sizeof(bn)) 518 == FALSE) 519 return (FALSE); 520 free(curbp->b_bname); 521 if ((curbp->b_bname = strdup(bn)) == NULL) 522 return (FALSE); 523 curbp->b_flag &= ~(BFBAK | BFCHG); 524 upmodes(curbp); 525 } 526 return (s); 527 } 528 529 /* 530 * Save the contents of the current buffer back into its associated file. 531 */ 532 #ifndef MAKEBACKUP 533 #define MAKEBACKUP TRUE 534 #endif /* !MAKEBACKUP */ 535 static int makebackup = MAKEBACKUP; 536 537 /* ARGSUSED */ 538 int 539 filesave(int f, int n) 540 { 541 return (buffsave(curbp)); 542 } 543 544 /* 545 * Save the contents of the buffer argument into its associated file. Do 546 * nothing if there have been no changes (is this a bug, or a feature?). 547 * Error if there is no remembered file name. If this is the first write 548 * since the read or visit, then a backup copy of the file is made. 549 * Allow user to select whether or not to make backup files by looking at 550 * the value of makebackup. 551 */ 552 int 553 buffsave(struct buffer *bp) 554 { 555 int s; 556 557 /* return, no changes */ 558 if ((bp->b_flag & BFCHG) == 0) { 559 ewprintf("(No changes need to be saved)"); 560 return (TRUE); 561 } 562 563 /* must have a name */ 564 if (bp->b_fname[0] == '\0') { 565 ewprintf("No file name"); 566 return (FALSE); 567 } 568 569 /* Ensure file has not been modified elsewhere */ 570 /* We don't use the ignore flag here */ 571 if (fchecktime(bp) != TRUE) { 572 if ((s = eyesno("File has changed on disk since last save. " 573 "Save anyway")) != TRUE) 574 return (s); 575 } 576 577 if (makebackup && (bp->b_flag & BFBAK)) { 578 s = fbackupfile(bp->b_fname); 579 /* hard error */ 580 if (s == ABORT) 581 return (FALSE); 582 /* softer error */ 583 if (s == FALSE && 584 (s = eyesno("Backup error, save anyway")) != TRUE) 585 return (s); 586 } 587 if ((s = writeout(bp, bp->b_fname)) == TRUE) { 588 bp->b_flag &= ~(BFCHG | BFBAK); 589 upmodes(bp); 590 } 591 return (s); 592 } 593 594 /* 595 * Since we don't have variables (we probably should) this is a command 596 * processor for changing the value of the make backup flag. If no argument 597 * is given, sets makebackup to true, so backups are made. If an argument is 598 * given, no backup files are made when saving a new version of a file. 599 */ 600 /* ARGSUSED */ 601 int 602 makebkfile(int f, int n) 603 { 604 if (f & FFARG) 605 makebackup = n > 0; 606 else 607 makebackup = !makebackup; 608 ewprintf("Backup files %sabled", makebackup ? "en" : "dis"); 609 return (TRUE); 610 } 611 612 /* 613 * NB: bp is passed to both ffwopen and ffclose because some 614 * attribute information may need to be updated at open time 615 * and others after the close. This is OS-dependent. Note 616 * that the ff routines are assumed to be able to tell whether 617 * the attribute information has been set up in this buffer 618 * or not. 619 */ 620 621 /* 622 * This function performs the details of file writing; writing the file 623 * in buffer bp to file fn. Uses the file management routines in the 624 * "fileio.c" package. Most of the grief is checking of some sort. 625 */ 626 int 627 writeout(struct buffer *bp, char *fn) 628 { 629 int s; 630 631 /* open writes message */ 632 if ((s = ffwopen(fn, bp)) != FIOSUC) 633 return (FALSE); 634 s = ffputbuf(bp); 635 if (s == FIOSUC) { 636 /* no write error */ 637 s = ffclose(bp); 638 if (s == FIOSUC) 639 ewprintf("Wrote %s", fn); 640 } else 641 /* ignore close error if it is a write error */ 642 (void)ffclose(bp); 643 (void)fupdstat(bp); 644 return (s == FIOSUC); 645 } 646 647 /* 648 * Tag all windows for bp (all windows if bp == NULL) as needing their 649 * mode line updated. 650 */ 651 void 652 upmodes(struct buffer *bp) 653 { 654 struct mgwin *wp; 655 656 for (wp = wheadp; wp != NULL; wp = wp->w_wndp) 657 if (bp == NULL || curwp->w_bufp == bp) 658 wp->w_rflag |= WFMODE; 659 } 660 661 /* 662 * Same as dirname, except an empty string is returned in 663 * place of "/". This means we can always add a trailing 664 * slash and be correct. 665 * Unlike dirname, we allocate. Caller must free. 666 */ 667 static char * 668 xdirname(const char *path) 669 { 670 char *dp; 671 672 dp = dirname(path); 673 if (*dp && dp[0] == '/' && dp[1] == '\0') 674 return (strdup("")); 675 676 return (strdup(dp)); 677 } 678