1/* $Id: files.c,v 1.348.2.18 2007/04/21 20:33:56 dolorous Exp $ */ 2/************************************************************************** 3 * files.c * 4 * * 5 * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 Chris Allegretta * 6 * Copyright (C) 2005, 2006, 2007 David Lawrence Ramsey * 7 * This program is free software; you can redistribute it and/or modify * 8 * it under the terms of the GNU General Public License as published by * 9 * the Free Software Foundation; either version 2, or (at your option) * 10 * any later version. * 11 * * 12 * This program is distributed in the hope that it will be useful, but * 13 * WITHOUT ANY WARRANTY; without even the implied warranty of * 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * 15 * General Public License for more details. * 16 * * 17 * You should have received a copy of the GNU General Public License * 18 * along with this program; if not, write to the Free Software * 19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 20 * 02110-1301, USA. * 21 * * 22 **************************************************************************/ 23 24#include "proto.h" 25 26#include <stdio.h> 27#include <string.h> 28#include <unistd.h> 29#include <utime.h> 30#include <fcntl.h> 31#include <errno.h> 32#include <ctype.h> 33#include <pwd.h> 34 35/* Add an entry to the openfile openfilestruct. This should only be 36 * called from open_buffer(). */ 37void make_new_buffer(void) 38{ 39 /* If there are no entries in openfile, make the first one and 40 * move to it. */ 41 if (openfile == NULL) { 42 openfile = make_new_opennode(); 43 splice_opennode(openfile, openfile, openfile); 44 /* Otherwise, make a new entry for openfile, splice it in after 45 * the current entry, and move to it. */ 46 } else { 47 splice_opennode(openfile, make_new_opennode(), openfile->next); 48 openfile = openfile->next; 49 } 50 51 /* Initialize the new buffer. */ 52 initialize_buffer(); 53} 54 55/* Initialize the current entry of the openfile openfilestruct. */ 56void initialize_buffer(void) 57{ 58 assert(openfile != NULL); 59 60 openfile->filename = mallocstrcpy(NULL, ""); 61 62 initialize_buffer_text(); 63 64 openfile->current_x = 0; 65 openfile->placewewant = 0; 66 openfile->current_y = 0; 67 68 openfile->modified = FALSE; 69#ifndef NANO_TINY 70 openfile->mark_set = FALSE; 71 72 openfile->mark_begin = NULL; 73 openfile->mark_begin_x = 0; 74 75 openfile->fmt = NIX_FILE; 76 77 openfile->current_stat = NULL; 78#endif 79#ifdef ENABLE_COLOR 80 openfile->colorstrings = NULL; 81#endif 82} 83 84/* Initialize the text of the current entry of the openfile 85 * openfilestruct. */ 86void initialize_buffer_text(void) 87{ 88 assert(openfile != NULL); 89 90 openfile->fileage = make_new_node(NULL); 91 openfile->fileage->data = mallocstrcpy(NULL, ""); 92 93 openfile->filebot = openfile->fileage; 94 openfile->edittop = openfile->fileage; 95 openfile->current = openfile->fileage; 96 97 openfile->totsize = 0; 98} 99 100/* If it's not "", filename is a file to open. We make a new buffer, if 101 * necessary, and then open and read the file, if applicable. */ 102void open_buffer(const char *filename) 103{ 104 bool new_buffer = (openfile == NULL 105#ifdef ENABLE_MULTIBUFFER 106 || ISSET(MULTIBUFFER) 107#endif 108 ); 109 /* Whether we load into this buffer or a new one. */ 110 FILE *f; 111 int rc; 112 /* rc == -2 means that we have a new file. -1 means that the 113 * open() failed. 0 means that the open() succeeded. */ 114 115 assert(filename != NULL); 116 117#ifndef DISABLE_OPERATINGDIR 118 if (check_operating_dir(filename, FALSE)) { 119 statusbar(_("Can't insert file from outside of %s"), 120 operating_dir); 121 return; 122 } 123#endif 124 125 /* If the filename isn't blank, open the file. Otherwise, treat it 126 * as a new file. */ 127 rc = (filename[0] != '\0') ? open_file(filename, new_buffer, &f) : 128 -2; 129 130 /* If we're loading into a new buffer, add a new entry to 131 * openfile. */ 132 if (new_buffer) 133 make_new_buffer(); 134 135 /* If we have a file, and we're loading into a new buffer, update 136 * the filename. */ 137 if (rc != -1 && new_buffer) 138 openfile->filename = mallocstrcpy(openfile->filename, filename); 139 140 /* If we have a non-new file, read it in. Then, if the buffer has 141 * no stat, update the stat, if applicable. */ 142 if (rc == 0) { 143 read_file(f, filename); 144#ifndef NANO_TINY 145 if (openfile->current_stat == NULL) { 146 openfile->current_stat = 147 (struct stat *)nmalloc(sizeof(struct stat)); 148 stat(filename, openfile->current_stat); 149 } 150#endif 151 } 152 153 /* If we have a file, and we're loading into a new buffer, move back 154 * to the beginning of the first line of the buffer. */ 155 if (rc != -1 && new_buffer) { 156 openfile->current = openfile->fileage; 157 openfile->current_x = 0; 158 openfile->placewewant = 0; 159 } 160 161#ifdef ENABLE_COLOR 162 /* If we're loading into a new buffer, update the colors to account 163 * for it, if applicable. */ 164 if (new_buffer) 165 color_update(); 166#endif 167} 168 169#ifndef DISABLE_SPELLER 170/* If it's not "", filename is a file to open. We blow away the text of 171 * the current buffer, and then open and read the file, if 172 * applicable. Note that we skip the operating directory test when 173 * doing this. */ 174void replace_buffer(const char *filename) 175{ 176 FILE *f; 177 int rc; 178 /* rc == -2 means that we have a new file. -1 means that the 179 * open() failed. 0 means that the open() succeeded. */ 180 181 assert(filename != NULL); 182 183 /* If the filename isn't blank, open the file. Otherwise, treat it 184 * as a new file. */ 185 rc = (filename[0] != '\0') ? open_file(filename, TRUE, &f) : -2; 186 187 /* Reinitialize the text of the current buffer. */ 188 free_filestruct(openfile->fileage); 189 initialize_buffer_text(); 190 191 /* If we have a non-new file, read it in. */ 192 if (rc == 0) 193 read_file(f, filename); 194 195 /* Move back to the beginning of the first line of the buffer. */ 196 openfile->current = openfile->fileage; 197 openfile->current_x = 0; 198 openfile->placewewant = 0; 199} 200#endif /* !DISABLE_SPELLER */ 201 202/* Update the screen to account for the current buffer. */ 203void display_buffer(void) 204{ 205 /* Update the titlebar, since the filename may have changed. */ 206 titlebar(NULL); 207 208#ifdef ENABLE_COLOR 209 /* Make sure we're using the buffer's associated colors, if 210 * applicable. */ 211 color_init(); 212#endif 213 214 /* Update the edit window. */ 215 edit_refresh(); 216} 217 218#ifdef ENABLE_MULTIBUFFER 219/* Switch to the next file buffer if next_buf is TRUE. Otherwise, 220 * switch to the previous file buffer. */ 221void switch_to_prevnext_buffer(bool next_buf) 222{ 223 assert(openfile != NULL); 224 225 /* If only one file buffer is open, indicate it on the statusbar and 226 * get out. */ 227 if (openfile == openfile->next) { 228 statusbar(_("No more open file buffers")); 229 return; 230 } 231 232 /* Switch to the next or previous file buffer, depending on the 233 * value of next_buf. */ 234 openfile = next_buf ? openfile->next : openfile->prev; 235 236#ifdef DEBUG 237 fprintf(stderr, "filename is %s\n", openfile->filename); 238#endif 239 240 /* Update the screen to account for the current buffer. */ 241 display_buffer(); 242 243 /* Indicate the switch on the statusbar. */ 244 statusbar(_("Switched to %s"), 245 ((openfile->filename[0] == '\0') ? _("New Buffer") : 246 openfile->filename)); 247 248#ifdef DEBUG 249 dump_filestruct(openfile->current); 250#endif 251} 252 253/* Switch to the previous entry in the openfile filebuffer. */ 254void switch_to_prev_buffer_void(void) 255{ 256 switch_to_prevnext_buffer(FALSE); 257} 258 259/* Switch to the next entry in the openfile filebuffer. */ 260void switch_to_next_buffer_void(void) 261{ 262 switch_to_prevnext_buffer(TRUE); 263} 264 265/* Delete an entry from the openfile filebuffer, and switch to the one 266 * after it. Return TRUE on success, or FALSE if there are no more open 267 * file buffers. */ 268bool close_buffer(void) 269{ 270 assert(openfile != NULL); 271 272 /* If only one file buffer is open, get out. */ 273 if (openfile == openfile->next) 274 return FALSE; 275 276 /* Switch to the next file buffer. */ 277 switch_to_next_buffer_void(); 278 279 /* Close the file buffer we had open before. */ 280 unlink_opennode(openfile->prev); 281 282 display_main_list(); 283 284 return TRUE; 285} 286#endif /* ENABLE_MULTIBUFFER */ 287 288/* We make a new line of text from buf. buf is length buf_len. If 289 * first_line_ins is TRUE, then we put the new line at the top of the 290 * file. Otherwise, we assume prevnode is the last line of the file, 291 * and put our line after prevnode. */ 292filestruct *read_line(char *buf, filestruct *prevnode, bool 293 *first_line_ins, size_t buf_len) 294{ 295 filestruct *fileptr = (filestruct *)nmalloc(sizeof(filestruct)); 296 297 /* Convert nulls to newlines. buf_len is the string's real 298 * length. */ 299 unsunder(buf, buf_len); 300 301 assert(openfile->fileage != NULL && strlen(buf) == buf_len); 302 303 fileptr->data = mallocstrcpy(NULL, buf); 304 305#ifndef NANO_TINY 306 /* If it's a DOS file ("\r\n"), and file conversion isn't disabled, 307 * strip the '\r' part from fileptr->data. */ 308 if (!ISSET(NO_CONVERT) && buf_len > 0 && buf[buf_len - 1] == '\r') 309 fileptr->data[buf_len - 1] = '\0'; 310#endif 311 312 if (*first_line_ins) { 313 /* Special case: We're inserting with the cursor on the first 314 * line. */ 315 fileptr->prev = NULL; 316 fileptr->next = openfile->fileage; 317 fileptr->lineno = 1; 318 if (*first_line_ins) { 319 *first_line_ins = FALSE; 320 /* If we're inserting into the first line of the file, then 321 * we want to make sure that our edit buffer stays on the 322 * first line and that fileage stays up to date. */ 323 openfile->edittop = fileptr; 324 } else 325 openfile->filebot = fileptr; 326 openfile->fileage = fileptr; 327 } else { 328 assert(prevnode != NULL); 329 330 fileptr->prev = prevnode; 331 fileptr->next = NULL; 332 fileptr->lineno = prevnode->lineno + 1; 333 prevnode->next = fileptr; 334 } 335 336 return fileptr; 337} 338 339/* Read an open file into the current buffer. f should be set to the 340 * open file, and filename should be set to the name of the file. */ 341void read_file(FILE *f, const char *filename) 342{ 343 size_t num_lines = 0; 344 /* The number of lines in the file. */ 345 size_t len = 0; 346 /* The length of the current line of the file. */ 347 size_t i = 0; 348 /* The position in the current line of the file. */ 349 size_t bufx = MAX_BUF_SIZE; 350 /* The size of each chunk of the file that we read. */ 351 char input = '\0'; 352 /* The current input character. */ 353 char *buf; 354 /* The buffer where we store chunks of the file. */ 355 filestruct *fileptr = openfile->current; 356 /* The current line of the file. */ 357 bool first_line_ins = FALSE; 358 /* Whether we're inserting with the cursor on the first line. */ 359 int input_int; 360 /* The current value we read from the file, whether an input 361 * character or EOF. */ 362#ifndef NANO_TINY 363 int format = 0; 364 /* 0 = *nix, 1 = DOS, 2 = Mac, 3 = both DOS and Mac. */ 365#endif 366 367 assert(openfile->fileage != NULL && openfile->current != NULL); 368 369 buf = charalloc(bufx); 370 buf[0] = '\0'; 371 372 if (openfile->current == openfile->fileage) 373 first_line_ins = TRUE; 374 else 375 fileptr = openfile->current->prev; 376 377 /* Read the entire file into the filestruct. */ 378 while ((input_int = getc(f)) != EOF) { 379 input = (char)input_int; 380 381 /* If it's a *nix file ("\n") or a DOS file ("\r\n"), and file 382 * conversion isn't disabled, handle it! */ 383 if (input == '\n') { 384#ifndef NANO_TINY 385 /* If there's a '\r' before the '\n', set format to DOS if 386 * we currently think this is a *nix file, or to both if we 387 * currently think it's a Mac file. */ 388 if (!ISSET(NO_CONVERT) && i > 0 && buf[i - 1] == '\r' && 389 (format == 0 || format == 2)) 390 format++; 391#endif 392 393 /* Read in the line properly. */ 394 fileptr = read_line(buf, fileptr, &first_line_ins, len); 395 396 /* Reset the line length in preparation for the next 397 * line. */ 398 len = 0; 399 400 num_lines++; 401 buf[0] = '\0'; 402 i = 0; 403#ifndef NANO_TINY 404 /* If it's a Mac file ('\r' without '\n'), and file conversion 405 * isn't disabled, handle it! */ 406 } else if (!ISSET(NO_CONVERT) && i > 0 && buf[i - 1] == '\r') { 407 /* If we currently think the file is a *nix file, set format 408 * to Mac. If we currently think the file is a DOS file, 409 * set format to both DOS and Mac. */ 410 if (format == 0 || format == 1) 411 format += 2; 412 413 /* Read in the line properly. */ 414 fileptr = read_line(buf, fileptr, &first_line_ins, len); 415 416 /* Reset the line length in preparation for the next line. 417 * Since we've already read in the next character, reset it 418 * to 1 instead of 0. */ 419 len = 1; 420 421 num_lines++; 422 buf[0] = input; 423 buf[1] = '\0'; 424 i = 1; 425#endif 426 } else { 427 /* Calculate the total length of the line. It might have 428 * nulls in it, so we can't just use strlen() here. */ 429 len++; 430 431 /* Now we allocate a bigger buffer MAX_BUF_SIZE characters 432 * at a time. If we allocate a lot of space for one line, 433 * we may indeed have to use a buffer this big later on, so 434 * we don't decrease it at all. We do free it at the end, 435 * though. */ 436 if (i >= bufx - 1) { 437 bufx += MAX_BUF_SIZE; 438 buf = charealloc(buf, bufx); 439 } 440 441 buf[i] = input; 442 buf[i + 1] = '\0'; 443 i++; 444 } 445 } 446 447 /* Perhaps this could use some better handling. */ 448 if (ferror(f)) 449 nperror(filename); 450 fclose(f); 451 452#ifndef NANO_TINY 453 /* If file conversion isn't disabled and the last character in this 454 * file is '\r', read it in properly as a Mac format line. */ 455 if (len == 0 && !ISSET(NO_CONVERT) && input == '\r') { 456 len = 1; 457 458 buf[0] = input; 459 buf[1] = '\0'; 460 } 461#endif 462 463 /* Did we not get a newline and still have stuff to do? */ 464 if (len > 0) { 465#ifndef NANO_TINY 466 /* If file conversion isn't disabled and the last character in 467 * this file is '\r', set format to Mac if we currently think 468 * the file is a *nix file, or to both DOS and Mac if we 469 * currently think the file is a DOS file. */ 470 if (!ISSET(NO_CONVERT) && buf[len - 1] == '\r' && 471 (format == 0 || format == 1)) 472 format += 2; 473#endif 474 475 /* Read in the last line properly. */ 476 fileptr = read_line(buf, fileptr, &first_line_ins, len); 477 num_lines++; 478 } 479 480 free(buf); 481 482 /* If we didn't get a file and we don't already have one, open a 483 * blank buffer. */ 484 if (fileptr == NULL) 485 open_buffer(""); 486 487 /* Attach the file we got to the filestruct. If we got a file of 488 * zero bytes, don't do anything. */ 489 if (num_lines > 0) { 490 /* If the file we got doesn't end in a newline, tack its last 491 * line onto the beginning of the line at current. */ 492 if (len > 0) { 493 size_t current_len = strlen(openfile->current->data); 494 495 /* Adjust the current x-coordinate to compensate for the 496 * change in the current line. */ 497 if (num_lines == 1) 498 openfile->current_x += len; 499 else 500 openfile->current_x = len; 501 502 /* Tack the text at fileptr onto the beginning of the text 503 * at current. */ 504 openfile->current->data = 505 charealloc(openfile->current->data, len + 506 current_len + 1); 507 charmove(openfile->current->data + len, 508 openfile->current->data, current_len + 1); 509 strncpy(openfile->current->data, fileptr->data, len); 510 511 /* Don't destroy fileage, edittop, or filebot! */ 512 if (fileptr == openfile->fileage) 513 openfile->fileage = openfile->current; 514 if (fileptr == openfile->edittop) 515 openfile->edittop = openfile->current; 516 if (fileptr == openfile->filebot) 517 openfile->filebot = openfile->current; 518 519 /* Move fileptr back one line and blow away the old fileptr, 520 * since its text has been saved. */ 521 fileptr = fileptr->prev; 522 if (fileptr != NULL) { 523 if (fileptr->next != NULL) 524 free(fileptr->next); 525 } 526 } 527 528 /* Attach the line at current after the line at fileptr. */ 529 if (fileptr != NULL) { 530 fileptr->next = openfile->current; 531 openfile->current->prev = fileptr; 532 } 533 534 /* Renumber starting with the last line of the file we 535 * inserted. */ 536 renumber(openfile->current); 537 } 538 539 openfile->totsize += get_totsize(openfile->fileage, 540 openfile->filebot); 541 542 /* If the NO_NEWLINES flag isn't set, and text has been added to 543 * the magicline (i.e. a file that doesn't end in a newline has been 544 * inserted at the end of the current buffer), add a new magicline, 545 * and move the current line down to it. */ 546 if (!ISSET(NO_NEWLINES) && openfile->filebot->data[0] != '\0') { 547 new_magicline(); 548 openfile->current = openfile->filebot; 549 openfile->current_x = 0; 550 } 551 552 /* Set the current place we want to the end of the last line of the 553 * file we inserted. */ 554 openfile->placewewant = xplustabs(); 555 556#ifndef NANO_TINY 557 if (format == 3) 558 statusbar( 559 P_("Read %lu line (Converted from DOS and Mac format)", 560 "Read %lu lines (Converted from DOS and Mac format)", 561 (unsigned long)num_lines), (unsigned long)num_lines); 562 else if (format == 2) { 563 openfile->fmt = MAC_FILE; 564 statusbar(P_("Read %lu line (Converted from Mac format)", 565 "Read %lu lines (Converted from Mac format)", 566 (unsigned long)num_lines), (unsigned long)num_lines); 567 } else if (format == 1) { 568 openfile->fmt = DOS_FILE; 569 statusbar(P_("Read %lu line (Converted from DOS format)", 570 "Read %lu lines (Converted from DOS format)", 571 (unsigned long)num_lines), (unsigned long)num_lines); 572 } else 573#endif 574 statusbar(P_("Read %lu line", "Read %lu lines", 575 (unsigned long)num_lines), (unsigned long)num_lines); 576} 577 578/* Open the file (and decide if it exists). If newfie is TRUE, display 579 * "New File" if the file is missing. Otherwise, say "[filename] not 580 * found". 581 * 582 * Return -2 if we say "New File", -1 if the file isn't opened, and 0 583 * otherwise. The file might still have an error while reading with a 0 584 * return value. *f is set to the opened file. */ 585int open_file(const char *filename, bool newfie, FILE **f) 586{ 587 struct stat fileinfo; 588 int fd; 589 char *full_filename; 590 591 assert(filename != NULL && f != NULL); 592 593 /* Get the specified file's full path. */ 594 full_filename = get_full_path(filename); 595 596 if (full_filename == NULL) 597 full_filename = mallocstrcpy(NULL, filename); 598 599 if (stat(full_filename, &fileinfo) == -1) { 600 if (newfie) { 601 statusbar(_("New File")); 602 return -2; 603 } 604 statusbar(_("\"%s\" not found"), filename); 605 beep(); 606 return -1; 607 } else if (S_ISDIR(fileinfo.st_mode) || S_ISCHR(fileinfo.st_mode) || 608 S_ISBLK(fileinfo.st_mode)) { 609 /* Don't open directories, character files, or block files. 610 * Sorry, /dev/sndstat! */ 611 statusbar(S_ISDIR(fileinfo.st_mode) ? 612 _("\"%s\" is a directory") : 613 _("\"%s\" is a device file"), filename); 614 beep(); 615 return -1; 616 } else if ((fd = open(full_filename, O_RDONLY)) == -1) { 617 statusbar(_("Error reading %s: %s"), filename, 618 strerror(errno)); 619 beep(); 620 return -1; 621 } else { 622 /* The file is A-OK. Open it. */ 623 *f = fdopen(fd, "rb"); 624 625 if (*f == NULL) { 626 statusbar(_("Error reading %s: %s"), filename, 627 strerror(errno)); 628 beep(); 629 close(fd); 630 } else 631 statusbar(_("Reading File")); 632 } 633 634 free(full_filename); 635 636 return 0; 637} 638 639/* This function will return the name of the first available extension 640 * of a filename (starting with [name][suffix], then [name][suffix].1, 641 * etc.). Memory is allocated for the return value. If no writable 642 * extension exists, we return "". */ 643char *get_next_filename(const char *name, const char *suffix) 644{ 645 static int ulmax_digits = -1; 646 unsigned long i = 0; 647 char *buf; 648 size_t namelen, suffixlen; 649 650 assert(name != NULL && suffix != NULL); 651 652 if (ulmax_digits == -1) 653 ulmax_digits = digits(ULONG_MAX); 654 655 namelen = strlen(name); 656 suffixlen = strlen(suffix); 657 658 buf = charalloc(namelen + suffixlen + ulmax_digits + 2); 659 sprintf(buf, "%s%s", name, suffix); 660 661 while (TRUE) { 662 struct stat fs; 663 664 if (stat(buf, &fs) == -1) 665 return buf; 666 if (i == ULONG_MAX) 667 break; 668 669 i++; 670 sprintf(buf + namelen + suffixlen, ".%lu", i); 671 } 672 673 /* We get here only if there is no possible save file. Blank out 674 * the filename to indicate this. */ 675 null_at(&buf, 0); 676 677 return buf; 678} 679 680/* Insert a file into a new buffer if the MULTIBUFFER flag is set, or 681 * into the current buffer if it isn't. If execute is TRUE, insert the 682 * output of an executed command instead of a file. */ 683void do_insertfile( 684#ifndef NANO_TINY 685 bool execute 686#else 687 void 688#endif 689 ) 690{ 691 int i; 692 const char *msg; 693 char *ans = mallocstrcpy(NULL, ""); 694 /* The last answer the user typed at the statusbar prompt. */ 695 filestruct *edittop_save = openfile->edittop; 696 size_t current_x_save = openfile->current_x; 697 ssize_t current_y_save = openfile->current_y; 698 bool at_edittop = FALSE; 699 /* Whether we're at the top of the edit window. */ 700 701 while (TRUE) { 702#ifndef NANO_TINY 703 if (execute) { 704 msg = 705#ifdef ENABLE_MULTIBUFFER 706 ISSET(MULTIBUFFER) ? 707 _("Command to execute in new buffer [from %s] ") : 708#endif 709 _("Command to execute [from %s] "); 710 } else { 711#endif 712 msg = 713#ifdef ENABLE_MULTIBUFFER 714 ISSET(MULTIBUFFER) ? 715 _("File to insert into new buffer [from %s] ") : 716#endif 717 _("File to insert [from %s] "); 718#ifndef NANO_TINY 719 } 720#endif 721 722 i = do_prompt(TRUE, 723#ifndef DISABLE_TABCOMP 724 TRUE, 725#endif 726#ifndef NANO_TINY 727 execute ? extcmd_list : 728#endif 729 insertfile_list, ans, 730#ifndef NANO_TINY 731 NULL, 732#endif 733 edit_refresh, msg, 734#ifndef DISABLE_OPERATINGDIR 735 operating_dir != NULL && strcmp(operating_dir, 736 ".") != 0 ? operating_dir : 737#endif 738 "./"); 739 740 /* If we're in multibuffer mode and the filename or command is 741 * blank, open a new buffer instead of canceling. If the 742 * filename or command begins with a newline (i.e. an encoded 743 * null), treat it as though it's blank. */ 744 if (i == -1 || ((i == -2 || answer[0] == '\n') 745#ifdef ENABLE_MULTIBUFFER 746 && !ISSET(MULTIBUFFER) 747#endif 748 )) { 749 statusbar(_("Cancelled")); 750 break; 751 } else { 752 size_t pww_save = openfile->placewewant; 753 754 ans = mallocstrcpy(ans, answer); 755 756#ifndef NANO_TINY 757#ifdef ENABLE_MULTIBUFFER 758 if (i == TOGGLE_MULTIBUFFER_KEY) { 759 /* Don't allow toggling if we're in view mode. */ 760 if (!ISSET(VIEW_MODE)) 761 TOGGLE(MULTIBUFFER); 762 continue; 763 } else 764#endif 765 if (i == NANO_TOOTHERINSERT_KEY) { 766 execute = !execute; 767 continue; 768 } 769#ifndef DISABLE_BROWSER 770 else 771#endif 772#endif /* !NANO_TINY */ 773 774#ifndef DISABLE_BROWSER 775 if (i == NANO_TOFILES_KEY) { 776 char *tmp = do_browse_from(answer); 777 778 if (tmp == NULL) 779 continue; 780 781 /* We have a file now. Indicate this. */ 782 free(answer); 783 answer = tmp; 784 785 i = 0; 786 } 787#endif 788 789 /* If we don't have a file yet, go back to the statusbar 790 * prompt. */ 791 if (i != 0 792#ifdef ENABLE_MULTIBUFFER 793 && (i != -2 || !ISSET(MULTIBUFFER)) 794#endif 795 ) 796 continue; 797 798#ifdef ENABLE_MULTIBUFFER 799 if (!ISSET(MULTIBUFFER)) { 800#endif 801 /* If we're not inserting into a new buffer, partition 802 * the filestruct so that it contains no text and hence 803 * looks like a new buffer, and keep track of whether 804 * the top of the partition is the top of the edit 805 * window. */ 806 filepart = partition_filestruct(openfile->current, 807 openfile->current_x, openfile->current, 808 openfile->current_x); 809 at_edittop = 810 (openfile->fileage == openfile->edittop); 811#ifdef ENABLE_MULTIBUFFER 812 } 813#endif 814 815 /* Convert newlines to nulls, just before we insert the file 816 * or execute the command. */ 817 sunder(answer); 818 align(&answer); 819 820#ifndef NANO_TINY 821 if (execute) { 822#ifdef ENABLE_MULTIBUFFER 823 if (ISSET(MULTIBUFFER)) 824 /* Open a blank buffer. */ 825 open_buffer(""); 826#endif 827 828 /* Save the command's output in the current buffer. */ 829 execute_command(answer); 830 831#ifdef ENABLE_MULTIBUFFER 832 if (ISSET(MULTIBUFFER)) { 833 /* Move back to the beginning of the first line of 834 * the buffer. */ 835 openfile->current = openfile->fileage; 836 openfile->current_x = 0; 837 openfile->placewewant = 0; 838 } 839#endif 840 } else { 841#endif /* !NANO_TINY */ 842 /* Make sure the path to the file specified in answer is 843 * tilde-expanded. */ 844 answer = mallocstrassn(answer, 845 real_dir_from_tilde(answer)); 846 847 /* Save the file specified in answer in the current 848 * buffer. */ 849 open_buffer(answer); 850#ifndef NANO_TINY 851 } 852#endif 853 854#ifdef ENABLE_MULTIBUFFER 855 if (ISSET(MULTIBUFFER)) 856 /* Update the screen to account for the current 857 * buffer. */ 858 display_buffer(); 859 else 860#endif 861 { 862 filestruct *top_save = openfile->fileage; 863 864 /* If we were at the top of the edit window before, set 865 * the saved value of edittop to the new top of the edit 866 * window. */ 867 if (at_edittop) 868 edittop_save = openfile->fileage; 869 870 /* Update the current x-coordinate to account for the 871 * number of characters inserted on the current line. */ 872 openfile->current_x = strlen(openfile->filebot->data); 873 if (openfile->fileage == openfile->filebot) 874 openfile->current_x += current_x_save; 875 876 /* Update the current y-coordinate to account for the 877 * number of lines inserted. */ 878 openfile->current_y += current_y_save; 879 880 /* Unpartition the filestruct so that it contains all 881 * the text again. Note that we've replaced the 882 * non-text originally in the partition with the text in 883 * the inserted file/executed command output. */ 884 unpartition_filestruct(&filepart); 885 886 /* Renumber starting with the beginning line of the old 887 * partition. */ 888 renumber(top_save); 889 890 /* Restore the old edittop. */ 891 openfile->edittop = edittop_save; 892 893 /* Restore the old place we want. */ 894 openfile->placewewant = pww_save; 895 896 /* Mark the file as modified. */ 897 set_modified(); 898 899 /* Update the screen. */ 900 edit_refresh(); 901 } 902 903 break; 904 } 905 } 906 907 free(ans); 908} 909 910/* Insert a file into a new buffer or the current buffer, depending on 911 * whether the MULTIBUFFER flag is set. If we're in view mode, only 912 * allow inserting a file into a new buffer. */ 913void do_insertfile_void(void) 914{ 915#ifdef ENABLE_MULTIBUFFER 916 if (ISSET(VIEW_MODE) && !ISSET(MULTIBUFFER)) 917 statusbar(_("Key invalid in non-multibuffer mode")); 918 else 919#endif 920 do_insertfile( 921#ifndef NANO_TINY 922 FALSE 923#endif 924 ); 925 926 display_main_list(); 927} 928 929/* When passed "[relative path]" or "[relative path][filename]" in 930 * origpath, return "[full path]" or "[full path][filename]" on success, 931 * or NULL on error. Do this if the file doesn't exist but the relative 932 * path does, since the file could exist in memory but not yet on disk). 933 * Don't do this if the relative path doesn't exist, since we won't be 934 * able to go there. */ 935char *get_full_path(const char *origpath) 936{ 937 struct stat fileinfo; 938 char *d_here, *d_there, *d_there_file = NULL; 939 const char *last_slash; 940 bool path_only; 941 942 if (origpath == NULL) 943 return NULL; 944 945 /* Get the current directory. If it doesn't exist, back up and try 946 * again until we get a directory that does, and use that as the 947 * current directory. */ 948 d_here = charalloc(PATH_MAX + 1); 949 d_here = getcwd(d_here, PATH_MAX + 1); 950 951 while (d_here == NULL) { 952 if (chdir("..") == -1) 953 break; 954 955 d_here = getcwd(d_here, PATH_MAX + 1); 956 } 957 958 /* If we succeeded, canonicalize it in d_here. */ 959 if (d_here != NULL) { 960 align(&d_here); 961 962 /* If the current directory isn't "/", tack a slash onto the end 963 * of it. */ 964 if (strcmp(d_here, "/") != 0) { 965 d_here = charealloc(d_here, strlen(d_here) + 2); 966 strcat(d_here, "/"); 967 } 968 /* Otherwise, set d_here to "". */ 969 } else 970 d_here = mallocstrcpy(NULL, ""); 971 972 d_there = real_dir_from_tilde(origpath); 973 974 /* If stat()ing d_there fails, assume that d_there refers to a new 975 * file that hasn't been saved to disk yet. Set path_only to TRUE 976 * if d_there refers to a directory, and FALSE otherwise. */ 977 path_only = (stat(d_there, &fileinfo) != -1 && 978 S_ISDIR(fileinfo.st_mode)); 979 980 /* If path_only is TRUE, make sure d_there ends in a slash. */ 981 if (path_only) { 982 size_t d_there_len = strlen(d_there); 983 984 if (d_there[d_there_len - 1] != '/') { 985 d_there = charealloc(d_there, d_there_len + 2); 986 strcat(d_there, "/"); 987 } 988 } 989 990 /* Search for the last slash in d_there. */ 991 last_slash = strrchr(d_there, '/'); 992 993 /* If we didn't find one, then make sure the answer is in the format 994 * "d_here/d_there". */ 995 if (last_slash == NULL) { 996 assert(!path_only); 997 998 d_there_file = d_there; 999 d_there = d_here; 1000 } else { 1001 /* If path_only is FALSE, then save the filename portion of the 1002 * answer (everything after the last slash) in d_there_file. */ 1003 if (!path_only) 1004 d_there_file = mallocstrcpy(NULL, last_slash + 1); 1005 1006 /* Remove the filename portion of the answer from d_there. */ 1007 null_at(&d_there, last_slash - d_there + 1); 1008 1009 /* Go to the path specified in d_there. */ 1010 if (chdir(d_there) == -1) { 1011 free(d_there); 1012 d_there = NULL; 1013 } else { 1014 free(d_there); 1015 1016 /* Get the full path. */ 1017 d_there = charalloc(PATH_MAX + 1); 1018 d_there = getcwd(d_there, PATH_MAX + 1); 1019 1020 /* If we succeeded, canonicalize it in d_there. */ 1021 if (d_there != NULL) { 1022 align(&d_there); 1023 1024 /* If the current directory isn't "/", tack a slash onto 1025 * the end of it. */ 1026 if (strcmp(d_there, "/") != 0) { 1027 d_there = charealloc(d_there, strlen(d_there) + 2); 1028 strcat(d_there, "/"); 1029 } 1030 } else 1031 /* Otherwise, set path_only to TRUE, so that we clean up 1032 * correctly, free all allocated memory, and return 1033 * NULL. */ 1034 path_only = TRUE; 1035 1036 /* Finally, go back to the path specified in d_here, 1037 * where we were before. We don't check for a chdir() 1038 * error, since we can do nothing if we get one. */ 1039 chdir(d_here); 1040 1041 /* Free d_here, since we're done using it. */ 1042 free(d_here); 1043 } 1044 } 1045 1046 /* At this point, if path_only is FALSE and d_there isn't NULL, 1047 * d_there contains the path portion of the answer and d_there_file 1048 * contains the filename portion of the answer. If this is the 1049 * case, tack the latter onto the end of the former. d_there will 1050 * then contain the complete answer. */ 1051 if (!path_only && d_there != NULL) { 1052 d_there = charealloc(d_there, strlen(d_there) + 1053 strlen(d_there_file) + 1); 1054 strcat(d_there, d_there_file); 1055 } 1056 1057 /* Free d_there_file, since we're done using it. */ 1058 if (d_there_file != NULL) 1059 free(d_there_file); 1060 1061 return d_there; 1062} 1063 1064/* Return the full version of path, as returned by get_full_path(). On 1065 * error, if path doesn't reference a directory, or if the directory 1066 * isn't writable, return NULL. */ 1067char *check_writable_directory(const char *path) 1068{ 1069 char *full_path = get_full_path(path); 1070 1071 /* If get_full_path() fails, return NULL. */ 1072 if (full_path == NULL) 1073 return NULL; 1074 1075 /* If we can't write to path or path isn't a directory, return 1076 * NULL. */ 1077 if (access(full_path, W_OK) != 0 || 1078 full_path[strlen(full_path) - 1] != '/') { 1079 free(full_path); 1080 return NULL; 1081 } 1082 1083 /* Otherwise, return the full path. */ 1084 return full_path; 1085} 1086 1087/* This function calls mkstemp(($TMPDIR|P_tmpdir|/tmp/)"nano.XXXXXX"). 1088 * On success, it returns the malloc()ed filename and corresponding FILE 1089 * stream, opened in "r+b" mode. On error, it returns NULL for the 1090 * filename and leaves the FILE stream unchanged. */ 1091char *safe_tempfile(FILE **f) 1092{ 1093 char *full_tempdir = NULL; 1094 const char *tmpdir_env; 1095 int fd; 1096 mode_t original_umask = 0; 1097 1098 assert(f != NULL); 1099 1100 /* If $TMPDIR is set, set tempdir to it, run it through 1101 * get_full_path(), and save the result in full_tempdir. Otherwise, 1102 * leave full_tempdir set to NULL. */ 1103 tmpdir_env = getenv("TMPDIR"); 1104 if (tmpdir_env != NULL) 1105 full_tempdir = check_writable_directory(tmpdir_env); 1106 1107 /* If $TMPDIR is unset, empty, or not a writable directory, and 1108 * full_tempdir is NULL, try P_tmpdir instead. */ 1109 if (full_tempdir == NULL) 1110 full_tempdir = check_writable_directory(P_tmpdir); 1111 1112 /* if P_tmpdir is NULL, use /tmp. */ 1113 if (full_tempdir == NULL) 1114 full_tempdir = mallocstrcpy(NULL, "/tmp/"); 1115 1116 full_tempdir = charealloc(full_tempdir, strlen(full_tempdir) + 12); 1117 strcat(full_tempdir, "nano.XXXXXX"); 1118 1119 original_umask = umask(0); 1120 umask(S_IRWXG | S_IRWXO); 1121 1122 fd = mkstemp(full_tempdir); 1123 1124 if (fd != -1) 1125 *f = fdopen(fd, "r+b"); 1126 else { 1127 free(full_tempdir); 1128 full_tempdir = NULL; 1129 } 1130 1131 umask(original_umask); 1132 1133 return full_tempdir; 1134} 1135 1136#ifndef DISABLE_OPERATINGDIR 1137/* Initialize full_operating_dir based on operating_dir. */ 1138void init_operating_dir(void) 1139{ 1140 assert(full_operating_dir == NULL); 1141 1142 if (operating_dir == NULL) 1143 return; 1144 1145 full_operating_dir = get_full_path(operating_dir); 1146 1147 /* If get_full_path() failed or the operating directory is 1148 * inaccessible, unset operating_dir. */ 1149 if (full_operating_dir == NULL || chdir(full_operating_dir) == -1) { 1150 free(full_operating_dir); 1151 full_operating_dir = NULL; 1152 free(operating_dir); 1153 operating_dir = NULL; 1154 } 1155} 1156 1157/* Check to see if we're inside the operating directory. Return FALSE 1158 * if we are, or TRUE otherwise. If allow_tabcomp is TRUE, allow 1159 * incomplete names that would be matches for the operating directory, 1160 * so that tab completion will work. */ 1161bool check_operating_dir(const char *currpath, bool allow_tabcomp) 1162{ 1163 /* full_operating_dir is global for memory cleanup. It should have 1164 * already been initialized by init_operating_dir(). Also, a 1165 * relative operating directory path will only be handled properly 1166 * if this is done. */ 1167 1168 char *fullpath; 1169 bool retval = FALSE; 1170 const char *whereami1, *whereami2 = NULL; 1171 1172 /* If no operating directory is set, don't bother doing anything. */ 1173 if (operating_dir == NULL) 1174 return FALSE; 1175 1176 assert(full_operating_dir != NULL); 1177 1178 fullpath = get_full_path(currpath); 1179 1180 /* If fullpath is NULL, it means some directory in the path doesn't 1181 * exist or is unreadable. If allow_tabcomp is FALSE, then currpath 1182 * is what the user typed somewhere. We don't want to report a 1183 * non-existent directory as being outside the operating directory, 1184 * so we return FALSE. If allow_tabcomp is TRUE, then currpath 1185 * exists, but is not executable. So we say it isn't in the 1186 * operating directory. */ 1187 if (fullpath == NULL) 1188 return allow_tabcomp; 1189 1190 whereami1 = strstr(fullpath, full_operating_dir); 1191 if (allow_tabcomp) 1192 whereami2 = strstr(full_operating_dir, fullpath); 1193 1194 /* If both searches failed, we're outside the operating directory. 1195 * Otherwise, check the search results. If the full operating 1196 * directory path is not at the beginning of the full current path 1197 * (for normal usage) and vice versa (for tab completion, if we're 1198 * allowing it), we're outside the operating directory. */ 1199 if (whereami1 != fullpath && whereami2 != full_operating_dir) 1200 retval = TRUE; 1201 free(fullpath); 1202 1203 /* Otherwise, we're still inside it. */ 1204 return retval; 1205} 1206#endif 1207 1208#ifndef NANO_TINY 1209void init_backup_dir(void) 1210{ 1211 char *full_backup_dir; 1212 1213 if (backup_dir == NULL) 1214 return; 1215 1216 full_backup_dir = get_full_path(backup_dir); 1217 1218 /* If get_full_path() failed or the backup directory is 1219 * inaccessible, unset backup_dir. */ 1220 if (full_backup_dir == NULL || 1221 full_backup_dir[strlen(full_backup_dir) - 1] != '/') { 1222 free(full_backup_dir); 1223 free(backup_dir); 1224 backup_dir = NULL; 1225 } else { 1226 free(backup_dir); 1227 backup_dir = full_backup_dir; 1228 } 1229} 1230#endif 1231 1232/* Read from inn, write to out. We assume inn is opened for reading, 1233 * and out for writing. We return 0 on success, -1 on read error, or -2 1234 * on write error. */ 1235int copy_file(FILE *inn, FILE *out) 1236{ 1237 char buf[BUFSIZ]; 1238 size_t charsread; 1239 int retval = 0; 1240 1241 assert(inn != NULL && out != NULL); 1242 1243 do { 1244 charsread = fread(buf, sizeof(char), BUFSIZ, inn); 1245 if (charsread == 0 && ferror(inn)) { 1246 retval = -1; 1247 break; 1248 } 1249 if (fwrite(buf, sizeof(char), charsread, out) < charsread) { 1250 retval = -2; 1251 break; 1252 } 1253 } while (charsread > 0); 1254 1255 if (fclose(inn) == EOF) 1256 retval = -1; 1257 if (fclose(out) == EOF) 1258 retval = -2; 1259 1260 return retval; 1261} 1262 1263/* Write a file out to disk. If f_open isn't NULL, we assume that it is 1264 * a stream associated with the file, and we don't try to open it 1265 * ourselves. If tmp is TRUE, we set the umask to disallow anyone else 1266 * from accessing the file, we don't set the filename to its name, and 1267 * we don't print out how many lines we wrote on the statusbar. 1268 * 1269 * tmp means we are writing a temporary file in a secure fashion. We 1270 * use it when spell checking or dumping the file on an error. If 1271 * append is APPEND, it means we are appending instead of overwriting. 1272 * If append is PREPEND, it means we are prepending instead of 1273 * overwriting. If nonamechange is TRUE, we don't change the current 1274 * filename. nonamechange is ignored if tmp is FALSE, we're appending, 1275 * or we're prepending. 1276 * 1277 * Return TRUE on success or FALSE on error. */ 1278bool write_file(const char *name, FILE *f_open, bool tmp, append_type 1279 append, bool nonamechange) 1280{ 1281 bool retval = FALSE; 1282 /* Instead of returning in this function, you should always 1283 * set retval and then goto cleanup_and_exit. */ 1284 size_t lineswritten = 0; 1285 const filestruct *fileptr = openfile->fileage; 1286 int fd; 1287 /* The file descriptor we use. */ 1288 mode_t original_umask = 0; 1289 /* Our umask, from when nano started. */ 1290 bool realexists; 1291 /* The result of stat(). TRUE if the file exists, FALSE 1292 * otherwise. If name is a link that points nowhere, realexists 1293 * is FALSE. */ 1294 struct stat st; 1295 /* The status fields filled in by stat(). */ 1296 bool anyexists; 1297 /* The result of lstat(). The same as realexists, unless name 1298 * is a link. */ 1299 struct stat lst; 1300 /* The status fields filled in by lstat(). */ 1301 char *realname; 1302 /* name after tilde expansion. */ 1303 FILE *f = NULL; 1304 /* The actual file, realname, we are writing to. */ 1305 char *tempname = NULL; 1306 /* The temp file name we write to on prepend. */ 1307 1308 assert(name != NULL); 1309 1310 if (name[0] == '\0') 1311 return -1; 1312 1313 if (f_open != NULL) 1314 f = f_open; 1315 1316 if (!tmp) 1317 titlebar(NULL); 1318 1319 realname = real_dir_from_tilde(name); 1320 1321#ifndef DISABLE_OPERATINGDIR 1322 /* If we're writing a temporary file, we're probably going outside 1323 * the operating directory, so skip the operating directory test. */ 1324 if (!tmp && check_operating_dir(realname, FALSE)) { 1325 statusbar(_("Can't write outside of %s"), operating_dir); 1326 goto cleanup_and_exit; 1327 } 1328#endif 1329 1330 anyexists = (lstat(realname, &lst) != -1); 1331 1332 /* If the temp file exists and isn't already open, give up. */ 1333 if (tmp && anyexists && f_open == NULL) 1334 goto cleanup_and_exit; 1335 1336 /* If NOFOLLOW_SYMLINKS is set, it doesn't make sense to prepend or 1337 * append to a symlink. Here we warn about the contradiction. */ 1338 if (ISSET(NOFOLLOW_SYMLINKS) && anyexists && S_ISLNK(lst.st_mode)) { 1339 statusbar( 1340 _("Cannot prepend or append to a symlink with --nofollow set")); 1341 goto cleanup_and_exit; 1342 } 1343 1344 /* Save the state of the file at the end of the symlink (if there is 1345 * one). */ 1346 realexists = (stat(realname, &st) != -1); 1347 1348#ifndef NANO_TINY 1349 /* We backup only if the backup toggle is set, the file isn't 1350 * temporary, and the file already exists. Furthermore, if we 1351 * aren't appending, prepending, or writing a selection, we backup 1352 * only if the file has not been modified by someone else since nano 1353 * opened it. */ 1354 if (ISSET(BACKUP_FILE) && !tmp && realexists && ((append != 1355 OVERWRITE || openfile->mark_set) || 1356 openfile->current_stat->st_mtime == st.st_mtime)) { 1357 FILE *backup_file; 1358 char *backupname; 1359 struct utimbuf filetime; 1360 int copy_status; 1361 1362 /* Save the original file's access and modification times. */ 1363 filetime.actime = openfile->current_stat->st_atime; 1364 filetime.modtime = openfile->current_stat->st_mtime; 1365 1366 if (f_open == NULL) { 1367 /* Open the original file to copy to the backup. */ 1368 f = fopen(realname, "rb"); 1369 1370 if (f == NULL) { 1371 statusbar(_("Error reading %s: %s"), realname, 1372 strerror(errno)); 1373 beep(); 1374 /* If we can't read from the original file, go on, since 1375 * only saving the original file is better than saving 1376 * nothing. */ 1377 goto skip_backup; 1378 } 1379 } 1380 1381 /* If backup_dir is set, we set backupname to 1382 * backup_dir/backupname~[.number], where backupname is the 1383 * canonicalized absolute pathname of realname with every '/' 1384 * replaced with a '!'. This means that /home/foo/file is 1385 * backed up in backup_dir/!home!foo!file~[.number]. */ 1386 if (backup_dir != NULL) { 1387 char *backuptemp = get_full_path(realname); 1388 1389 if (backuptemp == NULL) 1390 /* If get_full_path() failed, we don't have a 1391 * canonicalized absolute pathname, so just use the 1392 * filename portion of the pathname. We use tail() so 1393 * that e.g. ../backupname will be backed up in 1394 * backupdir/backupname~ instead of 1395 * backupdir/../backupname~. */ 1396 backuptemp = mallocstrcpy(NULL, tail(realname)); 1397 else { 1398 size_t i = 0; 1399 1400 for (; backuptemp[i] != '\0'; i++) { 1401 if (backuptemp[i] == '/') 1402 backuptemp[i] = '!'; 1403 } 1404 } 1405 1406 backupname = charalloc(strlen(backup_dir) + 1407 strlen(backuptemp) + 1); 1408 sprintf(backupname, "%s%s", backup_dir, backuptemp); 1409 free(backuptemp); 1410 backuptemp = get_next_filename(backupname, "~"); 1411 if (backuptemp[0] == '\0') { 1412 statusbar(_("Error writing %s: %s"), backupname, 1413 _("Too many backup files?")); 1414 free(backuptemp); 1415 free(backupname); 1416 /* If we can't write to the backup, go on, since only 1417 * saving the original file is better than saving 1418 * nothing. */ 1419 goto skip_backup; 1420 } else { 1421 free(backupname); 1422 backupname = backuptemp; 1423 } 1424 } else { 1425 backupname = charalloc(strlen(realname) + 2); 1426 sprintf(backupname, "%s~", realname); 1427 } 1428 1429 /* Open the destination backup file. Before we write to it, we 1430 * set its permissions, so no unauthorized person can read it as 1431 * we write. */ 1432 backup_file = fopen(backupname, "wb"); 1433 1434 if (backup_file == NULL || chmod(backupname, 1435 openfile->current_stat->st_mode) == -1) { 1436 statusbar(_("Error writing %s: %s"), backupname, 1437 strerror(errno)); 1438 free(backupname); 1439 if (backup_file != NULL) 1440 fclose(backup_file); 1441 /* If we can't write to the backup, go on, since only saving 1442 * the original file is better than saving nothing. */ 1443 goto skip_backup; 1444 } 1445 1446#ifdef DEBUG 1447 fprintf(stderr, "Backing up %s to %s\n", realname, backupname); 1448#endif 1449 1450 /* Copy the file. */ 1451 copy_status = copy_file(f, backup_file); 1452 1453 /* And set its metadata. */ 1454 if (copy_status != 0 || chown(backupname, 1455 openfile->current_stat->st_uid, 1456 openfile->current_stat->st_gid) == -1 || 1457 utime(backupname, &filetime) == -1) { 1458 if (copy_status == -1) { 1459 statusbar(_("Error reading %s: %s"), realname, 1460 strerror(errno)); 1461 beep(); 1462 } else 1463 statusbar(_("Error writing %s: %s"), backupname, 1464 strerror(errno)); 1465 /* If we can't read from or write to the backup, go on, 1466 * since only saving the original file is better than saving 1467 * nothing. */ 1468 } 1469 1470 free(backupname); 1471 } 1472 1473 skip_backup: 1474#endif /* !NANO_TINY */ 1475 1476 /* If NOFOLLOW_SYMLINKS is set and the file is a link, we aren't 1477 * doing prepend or append. So we delete the link first, and just 1478 * overwrite. */ 1479 if (ISSET(NOFOLLOW_SYMLINKS) && anyexists && S_ISLNK(lst.st_mode) && 1480 unlink(realname) == -1) { 1481 statusbar(_("Error writing %s: %s"), realname, strerror(errno)); 1482 goto cleanup_and_exit; 1483 } 1484 1485 if (f_open == NULL) { 1486 original_umask = umask(0); 1487 1488 /* If we create a temp file, we don't let anyone else access it. 1489 * We create a temp file if tmp is TRUE. */ 1490 if (tmp) 1491 umask(S_IRWXG | S_IRWXO); 1492 else 1493 umask(original_umask); 1494 } 1495 1496 /* If we're prepending, copy the file to a temp file. */ 1497 if (append == PREPEND) { 1498 int fd_source; 1499 FILE *f_source = NULL; 1500 1501 if (f == NULL) { 1502 f = fopen(realname, "rb"); 1503 1504 if (f == NULL) { 1505 statusbar(_("Error reading %s: %s"), realname, 1506 strerror(errno)); 1507 beep(); 1508 goto cleanup_and_exit; 1509 } 1510 } 1511 1512 tempname = safe_tempfile(&f); 1513 1514 if (tempname == NULL) { 1515 statusbar(_("Error writing temp file: %s"), 1516 strerror(errno)); 1517 goto cleanup_and_exit; 1518 } 1519 1520 if (f_open == NULL) { 1521 fd_source = open(realname, O_RDONLY | O_CREAT); 1522 1523 if (fd_source != -1) { 1524 f_source = fdopen(fd_source, "rb"); 1525 if (f_source == NULL) { 1526 statusbar(_("Error reading %s: %s"), realname, 1527 strerror(errno)); 1528 beep(); 1529 close(fd_source); 1530 fclose(f); 1531 unlink(tempname); 1532 goto cleanup_and_exit; 1533 } 1534 } 1535 } 1536 1537 if (copy_file(f_source, f) != 0) { 1538 statusbar(_("Error writing %s: %s"), tempname, 1539 strerror(errno)); 1540 unlink(tempname); 1541 goto cleanup_and_exit; 1542 } 1543 } 1544 1545 if (f_open == NULL) { 1546 /* Now open the file in place. Use O_EXCL if tmp is TRUE. This 1547 * is copied from joe, because wiggy says so *shrug*. */ 1548 fd = open(realname, O_WRONLY | O_CREAT | ((append == APPEND) ? 1549 O_APPEND : (tmp ? O_EXCL : O_TRUNC)), S_IRUSR | 1550 S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); 1551 1552 /* Set the umask back to the user's original value. */ 1553 umask(original_umask); 1554 1555 /* If we couldn't open the file, give up. */ 1556 if (fd == -1) { 1557 statusbar(_("Error writing %s: %s"), realname, 1558 strerror(errno)); 1559 1560 /* tempname has been set only if we're prepending. */ 1561 if (tempname != NULL) 1562 unlink(tempname); 1563 goto cleanup_and_exit; 1564 } 1565 1566 f = fdopen(fd, (append == APPEND) ? "ab" : "wb"); 1567 1568 if (f == NULL) { 1569 statusbar(_("Error writing %s: %s"), realname, 1570 strerror(errno)); 1571 close(fd); 1572 goto cleanup_and_exit; 1573 } 1574 } 1575 1576 /* There might not be a magicline. There won't be when writing out 1577 * a selection. */ 1578 assert(openfile->fileage != NULL && openfile->filebot != NULL); 1579 1580 while (fileptr != NULL) { 1581 size_t data_len = strlen(fileptr->data), size; 1582 1583 /* Convert newlines to nulls, just before we write to disk. */ 1584 sunder(fileptr->data); 1585 1586 size = fwrite(fileptr->data, sizeof(char), data_len, f); 1587 1588 /* Convert nulls to newlines. data_len is the string's real 1589 * length. */ 1590 unsunder(fileptr->data, data_len); 1591 1592 if (size < data_len) { 1593 statusbar(_("Error writing %s: %s"), realname, 1594 strerror(errno)); 1595 fclose(f); 1596 goto cleanup_and_exit; 1597 } 1598 1599 /* If we're on the last line of the file, don't write a newline 1600 * character after it. If the last line of the file is blank, 1601 * this means that zero bytes are written, in which case we 1602 * don't count the last line in the total lines written. */ 1603 if (fileptr == openfile->filebot) { 1604 if (fileptr->data[0] == '\0') 1605 lineswritten--; 1606 } else { 1607#ifndef NANO_TINY 1608 if (openfile->fmt == DOS_FILE || openfile->fmt == 1609 MAC_FILE) { 1610 if (putc('\r', f) == EOF) { 1611 statusbar(_("Error writing %s: %s"), realname, 1612 strerror(errno)); 1613 fclose(f); 1614 goto cleanup_and_exit; 1615 } 1616 } 1617 1618 if (openfile->fmt != MAC_FILE) { 1619#endif 1620 if (putc('\n', f) == EOF) { 1621 statusbar(_("Error writing %s: %s"), realname, 1622 strerror(errno)); 1623 fclose(f); 1624 goto cleanup_and_exit; 1625 } 1626#ifndef NANO_TINY 1627 } 1628#endif 1629 } 1630 1631 fileptr = fileptr->next; 1632 lineswritten++; 1633 } 1634 1635 /* If we're prepending, open the temp file, and append it to f. */ 1636 if (append == PREPEND) { 1637 int fd_source; 1638 FILE *f_source = NULL; 1639 1640 fd_source = open(tempname, O_RDONLY | O_CREAT); 1641 1642 if (fd_source != -1) { 1643 f_source = fdopen(fd_source, "rb"); 1644 if (f_source == NULL) 1645 close(fd_source); 1646 } 1647 1648 if (f_source == NULL) { 1649 statusbar(_("Error reading %s: %s"), tempname, 1650 strerror(errno)); 1651 beep(); 1652 fclose(f); 1653 goto cleanup_and_exit; 1654 } 1655 1656 if (copy_file(f_source, f) == -1 || unlink(tempname) == -1) { 1657 statusbar(_("Error writing %s: %s"), realname, 1658 strerror(errno)); 1659 goto cleanup_and_exit; 1660 } 1661 } else 1662 fclose(f); 1663 1664 if (!tmp && append == OVERWRITE) { 1665 if (!nonamechange) { 1666 openfile->filename = mallocstrcpy(openfile->filename, 1667 realname); 1668#ifdef ENABLE_COLOR 1669 /* We might have changed the filename, so update the colors 1670 * to account for it, and then make sure we're using 1671 * them. */ 1672 color_update(); 1673 color_init(); 1674 1675 /* If color syntaxes are available and turned on, we need to 1676 * call edit_refresh(). */ 1677 if (openfile->colorstrings != NULL && 1678 !ISSET(NO_COLOR_SYNTAX)) 1679 edit_refresh(); 1680#endif 1681 } 1682 1683#ifndef NANO_TINY 1684 /* Update current_stat to reference the file as it is now. */ 1685 if (openfile->current_stat == NULL) 1686 openfile->current_stat = 1687 (struct stat *)nmalloc(sizeof(struct stat)); 1688 stat(realname, openfile->current_stat); 1689#endif 1690 1691 statusbar(P_("Wrote %lu line", "Wrote %lu lines", 1692 (unsigned long)lineswritten), 1693 (unsigned long)lineswritten); 1694 openfile->modified = FALSE; 1695 titlebar(NULL); 1696 } 1697 1698 retval = TRUE; 1699 1700 cleanup_and_exit: 1701 free(realname); 1702 if (tempname != NULL) 1703 free(tempname); 1704 1705 return retval; 1706} 1707 1708#ifndef NANO_TINY 1709/* Write a marked selection from a file out to disk. Return TRUE on 1710 * success or FALSE on error. */ 1711bool write_marked_file(const char *name, FILE *f_open, bool tmp, 1712 append_type append) 1713{ 1714 bool retval; 1715 bool old_modified = openfile->modified; 1716 /* write_file() unsets the modified flag. */ 1717 bool added_magicline = FALSE; 1718 /* Whether we added a magicline after filebot. */ 1719 filestruct *top, *bot; 1720 size_t top_x, bot_x; 1721 1722 assert(openfile->mark_set); 1723 1724 /* Partition the filestruct so that it contains only the marked 1725 * text. */ 1726 mark_order((const filestruct **)&top, &top_x, 1727 (const filestruct **)&bot, &bot_x, NULL); 1728 filepart = partition_filestruct(top, top_x, bot, bot_x); 1729 1730 /* Handle the magicline if the NO_NEWLINES flag isn't set. If the 1731 * line at filebot is blank, treat it as the magicline and hence the 1732 * end of the file. Otherwise, add a magicline and treat it as the 1733 * end of the file. */ 1734 if (!ISSET(NO_NEWLINES) && 1735 (added_magicline = (openfile->filebot->data[0] != '\0'))) 1736 new_magicline(); 1737 1738 retval = write_file(name, f_open, tmp, append, TRUE); 1739 1740 /* If the NO_NEWLINES flag isn't set, and we added a magicline, 1741 * remove it now. */ 1742 if (!ISSET(NO_NEWLINES) && added_magicline) 1743 remove_magicline(); 1744 1745 /* Unpartition the filestruct so that it contains all the text 1746 * again. */ 1747 unpartition_filestruct(&filepart); 1748 1749 if (old_modified) 1750 set_modified(); 1751 1752 return retval; 1753} 1754#endif /* !NANO_TINY */ 1755 1756/* Write the current file to disk. If the mark is on, write the current 1757 * marked selection to disk. If exiting is TRUE, write the file to disk 1758 * regardless of whether the mark is on, and without prompting if the 1759 * TEMP_FILE flag is set. Return TRUE on success or FALSE on error. */ 1760bool do_writeout(bool exiting) 1761{ 1762 int i; 1763 append_type append = OVERWRITE; 1764 char *ans; 1765 /* The last answer the user typed at the statusbar prompt. */ 1766#ifdef NANO_EXTRA 1767 static bool did_credits = FALSE; 1768#endif 1769 bool retval = FALSE; 1770 1771 currshortcut = writefile_list; 1772 1773 if (exiting && openfile->filename[0] != '\0' && ISSET(TEMP_FILE)) { 1774 retval = write_file(openfile->filename, NULL, FALSE, OVERWRITE, 1775 FALSE); 1776 1777 /* Write succeeded. */ 1778 if (retval) 1779 return retval; 1780 } 1781 1782 ans = mallocstrcpy(NULL, 1783#ifndef NANO_TINY 1784 (!exiting && openfile->mark_set) ? "" : 1785#endif 1786 openfile->filename); 1787 1788 while (TRUE) { 1789 const char *msg; 1790#ifndef NANO_TINY 1791 const char *formatstr, *backupstr; 1792 1793 formatstr = (openfile->fmt == DOS_FILE) ? 1794 _(" [DOS Format]") : (openfile->fmt == MAC_FILE) ? 1795 _(" [Mac Format]") : ""; 1796 1797 backupstr = ISSET(BACKUP_FILE) ? _(" [Backup]") : ""; 1798 1799 /* If we're using restricted mode, don't display the "Write 1800 * Selection to File" prompt. This function is disabled, since 1801 * it allows reading from or writing to files not specified on 1802 * the command line. */ 1803 if (!ISSET(RESTRICTED) && !exiting && openfile->mark_set) 1804 msg = (append == PREPEND) ? 1805 _("Prepend Selection to File") : (append == APPEND) ? 1806 _("Append Selection to File") : 1807 _("Write Selection to File"); 1808 else 1809#endif /* !NANO_TINY */ 1810 msg = (append == PREPEND) ? _("File Name to Prepend to") : 1811 (append == APPEND) ? _("File Name to Append to") : 1812 _("File Name to Write"); 1813 1814 /* If we're using restricted mode, the filename isn't blank, 1815 * and we're at the "Write File" prompt, disable tab 1816 * completion. */ 1817 i = do_prompt(!ISSET(RESTRICTED) || 1818 openfile->filename[0] == '\0', 1819#ifndef DISABLE_TABCOMP 1820 TRUE, 1821#endif 1822 writefile_list, ans, 1823#ifndef NANO_TINY 1824 NULL, 1825#endif 1826 edit_refresh, "%s%s%s", msg, 1827#ifndef NANO_TINY 1828 formatstr, backupstr 1829#else 1830 "", "" 1831#endif 1832 ); 1833 1834 /* If the filename or command begins with a newline (i.e. an 1835 * encoded null), treat it as though it's blank. */ 1836 if (i < 0 || answer[0] == '\n') { 1837 statusbar(_("Cancelled")); 1838 retval = FALSE; 1839 break; 1840 } else { 1841 ans = mallocstrcpy(ans, answer); 1842 1843#ifndef DISABLE_BROWSER 1844 if (i == NANO_TOFILES_KEY) { 1845 char *tmp = do_browse_from(answer); 1846 1847 if (tmp == NULL) 1848 continue; 1849 1850 /* We have a file now. Indicate this. */ 1851 free(answer); 1852 answer = tmp; 1853 } else 1854#endif /* !DISABLE_BROWSER */ 1855#ifndef NANO_TINY 1856 if (i == TOGGLE_DOS_KEY) { 1857 openfile->fmt = (openfile->fmt == DOS_FILE) ? NIX_FILE : 1858 DOS_FILE; 1859 continue; 1860 } else if (i == TOGGLE_MAC_KEY) { 1861 openfile->fmt = (openfile->fmt == MAC_FILE) ? NIX_FILE : 1862 MAC_FILE; 1863 continue; 1864 } else if (i == TOGGLE_BACKUP_KEY) { 1865 TOGGLE(BACKUP_FILE); 1866 continue; 1867 } else 1868#endif /* !NANO_TINY */ 1869 if (i == NANO_PREPEND_KEY) { 1870 append = (append == PREPEND) ? OVERWRITE : PREPEND; 1871 continue; 1872 } else if (i == NANO_APPEND_KEY) { 1873 append = (append == APPEND) ? OVERWRITE : APPEND; 1874 continue; 1875 } 1876 1877#ifdef DEBUG 1878 fprintf(stderr, "filename is %s\n", answer); 1879#endif 1880 1881#ifdef NANO_EXTRA 1882 /* If the current file has been modified, we've pressed 1883 * Ctrl-X at the edit window to exit, we've pressed "y" at 1884 * the "Save modified buffer" prompt to save, we've entered 1885 * "zzy" as the filename to save under (hence "xyzzy"), and 1886 * this is the first time we've done this, show an Easter 1887 * egg. Display the credits. */ 1888 if (!did_credits && exiting && !ISSET(TEMP_FILE) && 1889 strcasecmp(answer, "zzy") == 0) { 1890 do_credits(); 1891 did_credits = TRUE; 1892 retval = FALSE; 1893 break; 1894 } 1895#endif 1896 1897 if (append == OVERWRITE) { 1898 size_t answer_len = strlen(answer); 1899 bool name_exists, do_warning; 1900 char *full_answer, *full_filename; 1901 struct stat st; 1902 1903 /* Convert newlines to nulls, just before we get the 1904 * full path. */ 1905 sunder(answer); 1906 1907 full_answer = get_full_path(answer); 1908 full_filename = get_full_path(openfile->filename); 1909 name_exists = (stat((full_answer == NULL) ? answer : 1910 full_answer, &st) != -1); 1911 if (openfile->filename[0] == '\0') 1912 do_warning = name_exists; 1913 else 1914 do_warning = (strcmp((full_answer == NULL) ? 1915 answer : full_answer, (full_filename == NULL) ? 1916 openfile->filename : full_filename) != 0); 1917 1918 /* Convert nulls to newlines. answer_len is the 1919 * string's real length. */ 1920 unsunder(answer, answer_len); 1921 1922 if (full_filename != NULL) 1923 free(full_filename); 1924 if (full_answer != NULL) 1925 free(full_answer); 1926 1927 if (do_warning) { 1928 /* If we're using restricted mode, we aren't allowed 1929 * to overwrite an existing file with the current 1930 * file. We also aren't allowed to change the name 1931 * of the current file if it has one, because that 1932 * would allow reading from or writing to files not 1933 * specified on the command line. */ 1934 if (ISSET(RESTRICTED)) 1935 continue; 1936 1937 if (name_exists) { 1938 i = do_yesno_prompt(FALSE, 1939 _("File exists, OVERWRITE ? ")); 1940 if (i == 0 || i == -1) 1941 continue; 1942 } else 1943#ifndef NANO_TINY 1944 if (exiting || !openfile->mark_set) 1945#endif 1946 { 1947 i = do_yesno_prompt(FALSE, 1948 _("Save file under DIFFERENT NAME ? ")); 1949 if (i == 0 || i == -1) 1950 continue; 1951 } 1952 } 1953 } 1954 1955 /* Convert newlines to nulls, just before we save the 1956 * file. */ 1957 sunder(answer); 1958 align(&answer); 1959 1960 /* Here's where we allow the selected text to be written to 1961 * a separate file. If we're using restricted mode, this 1962 * function is disabled, since it allows reading from or 1963 * writing to files not specified on the command line. */ 1964 retval = 1965#ifndef NANO_TINY 1966 (!ISSET(RESTRICTED) && !exiting && openfile->mark_set) ? 1967 write_marked_file(answer, NULL, FALSE, append) : 1968#endif 1969 write_file(answer, NULL, FALSE, append, FALSE); 1970 1971 break; 1972 } 1973 } 1974 1975 free(ans); 1976 1977 return retval; 1978} 1979 1980/* Write the current file to disk. If the mark is on, write the current 1981 * marked selection to disk. */ 1982void do_writeout_void(void) 1983{ 1984 do_writeout(FALSE); 1985 display_main_list(); 1986} 1987 1988/* Return a malloc()ed string containing the actual directory, used to 1989 * convert ~user/ and ~/ notation. */ 1990char *real_dir_from_tilde(const char *buf) 1991{ 1992 char *retval; 1993 1994 assert(buf != NULL); 1995 1996 if (buf[0] == '~') { 1997 size_t i = 1; 1998 char *tilde_dir; 1999 2000 /* Figure out how much of the string we need to compare. */ 2001 for (; buf[i] != '/' && buf[i] != '\0'; i++) 2002 ; 2003 2004 /* Get the home directory. */ 2005 if (i == 1) { 2006 get_homedir(); 2007 tilde_dir = mallocstrcpy(NULL, homedir); 2008 } else { 2009 const struct passwd *userdata; 2010 2011 tilde_dir = mallocstrncpy(NULL, buf, i + 1); 2012 tilde_dir[i] = '\0'; 2013 2014 do { 2015 userdata = getpwent(); 2016 } while (userdata != NULL && strcmp(userdata->pw_name, 2017 tilde_dir + 1) != 0); 2018 endpwent(); 2019 if (userdata != NULL) 2020 tilde_dir = mallocstrcpy(tilde_dir, userdata->pw_dir); 2021 } 2022 2023 retval = charalloc(strlen(tilde_dir) + strlen(buf + i) + 1); 2024 sprintf(retval, "%s%s", tilde_dir, buf + i); 2025 2026 free(tilde_dir); 2027 } else 2028 retval = mallocstrcpy(NULL, buf); 2029 2030 return retval; 2031} 2032 2033#if !defined(DISABLE_TABCOMP) || !defined(DISABLE_BROWSER) 2034/* Our sort routine for file listings. Sort alphabetically and 2035 * case-insensitively, and sort directories before filenames. */ 2036int diralphasort(const void *va, const void *vb) 2037{ 2038 struct stat fileinfo; 2039 const char *a = *(const char *const *)va; 2040 const char *b = *(const char *const *)vb; 2041 bool aisdir = stat(a, &fileinfo) != -1 && S_ISDIR(fileinfo.st_mode); 2042 bool bisdir = stat(b, &fileinfo) != -1 && S_ISDIR(fileinfo.st_mode); 2043 2044 if (aisdir && !bisdir) 2045 return -1; 2046 if (!aisdir && bisdir) 2047 return 1; 2048 2049 /* Standard function brain damage: We should be sorting 2050 * alphabetically and case-insensitively according to the current 2051 * locale, but there's no standard strcasecoll() function, so we 2052 * have to use multibyte strcasecmp() instead, */ 2053 return mbstrcasecmp(a, b); 2054} 2055 2056/* Free the memory allocated for array, which should contain len 2057 * elements. */ 2058void free_chararray(char **array, size_t len) 2059{ 2060 assert(array != NULL); 2061 2062 for (; len > 0; len--) 2063 free(array[len - 1]); 2064 free(array); 2065} 2066#endif 2067 2068#ifndef DISABLE_TABCOMP 2069/* Is the given path a directory? */ 2070bool is_dir(const char *buf) 2071{ 2072 char *dirptr; 2073 struct stat fileinfo; 2074 bool retval; 2075 2076 assert(buf != NULL); 2077 2078 dirptr = real_dir_from_tilde(buf); 2079 2080 retval = (stat(dirptr, &fileinfo) != -1 && 2081 S_ISDIR(fileinfo.st_mode)); 2082 2083 free(dirptr); 2084 2085 return retval; 2086} 2087 2088/* These functions, username_tab_completion(), cwd_tab_completion() 2089 * (originally exe_n_cwd_tab_completion()), and input_tab(), were 2090 * adapted from busybox 0.46 (cmdedit.c). Here is the notice from that 2091 * file: 2092 * 2093 * Termios command line History and Editting, originally 2094 * intended for NetBSD sh (ash) 2095 * Copyright (c) 1999 2096 * Main code: Adam Rogoyski <rogoyski@cs.utexas.edu> 2097 * Etc: Dave Cinege <dcinege@psychosis.com> 2098 * Majorly adjusted/re-written for busybox: 2099 * Erik Andersen <andersee@debian.org> 2100 * 2101 * You may use this code as you wish, so long as the original author(s) 2102 * are attributed in any redistributions of the source code. 2103 * This code is 'as is' with no warranty. 2104 * This code may safely be consumed by a BSD or GPL license. */ 2105 2106/* We consider the first buflen characters of buf for ~username tab 2107 * completion. */ 2108char **username_tab_completion(const char *buf, size_t *num_matches, 2109 size_t buflen) 2110{ 2111 char **matches = NULL; 2112 const struct passwd *userdata; 2113 2114 assert(buf != NULL && num_matches != NULL && buflen > 0); 2115 2116 *num_matches = 0; 2117 2118 while ((userdata = getpwent()) != NULL) { 2119 if (strncmp(userdata->pw_name, buf + 1, buflen - 1) == 0) { 2120 /* Cool, found a match. Add it to the list. This makes a 2121 * lot more sense to me (Chris) this way... */ 2122 2123#ifndef DISABLE_OPERATINGDIR 2124 /* ...unless the match exists outside the operating 2125 * directory, in which case just go to the next match. */ 2126 if (check_operating_dir(userdata->pw_dir, TRUE)) 2127 continue; 2128#endif 2129 2130 matches = (char **)nrealloc(matches, (*num_matches + 1) * 2131 sizeof(char *)); 2132 matches[*num_matches] = 2133 charalloc(strlen(userdata->pw_name) + 2); 2134 sprintf(matches[*num_matches], "~%s", userdata->pw_name); 2135 ++(*num_matches); 2136 } 2137 } 2138 endpwent(); 2139 2140 return matches; 2141} 2142 2143/* We consider the first buflen characters of buf for filename tab 2144 * completion. */ 2145char **cwd_tab_completion(const char *buf, bool allow_files, size_t 2146 *num_matches, size_t buflen) 2147{ 2148 char *dirname = mallocstrcpy(NULL, buf), *filename; 2149#ifndef DISABLE_OPERATINGDIR 2150 size_t dirnamelen; 2151#endif 2152 size_t filenamelen; 2153 char **matches = NULL; 2154 DIR *dir; 2155 const struct dirent *nextdir; 2156 2157 assert(dirname != NULL && num_matches != NULL && buflen >= 0); 2158 2159 *num_matches = 0; 2160 null_at(&dirname, buflen); 2161 2162 /* Okie, if there's a / in the buffer, strip out the directory 2163 * part. */ 2164 filename = strrchr(dirname, '/'); 2165 if (filename != NULL) { 2166 char *tmpdirname = filename + 1; 2167 2168 filename = mallocstrcpy(NULL, tmpdirname); 2169 *tmpdirname = '\0'; 2170 tmpdirname = dirname; 2171 dirname = real_dir_from_tilde(dirname); 2172 free(tmpdirname); 2173 } else { 2174 filename = dirname; 2175 dirname = mallocstrcpy(NULL, "./"); 2176 } 2177 2178 assert(dirname[strlen(dirname) - 1] == '/'); 2179 2180 dir = opendir(dirname); 2181 2182 if (dir == NULL) { 2183 /* Don't print an error, just shut up and return. */ 2184 beep(); 2185 free(filename); 2186 free(dirname); 2187 return NULL; 2188 } 2189 2190#ifndef DISABLE_OPERATINGDIR 2191 dirnamelen = strlen(dirname); 2192#endif 2193 filenamelen = strlen(filename); 2194 2195 while ((nextdir = readdir(dir)) != NULL) { 2196 bool skip_match = FALSE; 2197 2198#ifdef DEBUG 2199 fprintf(stderr, "Comparing \'%s\'\n", nextdir->d_name); 2200#endif 2201 /* See if this matches. */ 2202 if (strncmp(nextdir->d_name, filename, filenamelen) == 0 && 2203 (*filename == '.' || (strcmp(nextdir->d_name, ".") != 2204 0 && strcmp(nextdir->d_name, "..") != 0))) { 2205 /* Cool, found a match. Add it to the list. This makes a 2206 * lot more sense to me (Chris) this way... */ 2207 2208 char *tmp = charalloc(strlen(dirname) + 2209 strlen(nextdir->d_name) + 1); 2210 sprintf(tmp, "%s%s", dirname, nextdir->d_name); 2211 2212#ifndef DISABLE_OPERATINGDIR 2213 /* ...unless the match exists outside the operating 2214 * directory, in which case just go to the next match. */ 2215 if (check_operating_dir(tmp, TRUE)) 2216 skip_match = TRUE; 2217#endif 2218 2219 /* ...or unless the match isn't a directory and allow_files 2220 * isn't set, in which case just go to the next match. */ 2221 if (!allow_files && !is_dir(tmp)) 2222 skip_match = TRUE; 2223 2224 free(tmp); 2225 2226 if (skip_match) 2227 continue; 2228 2229 matches = (char **)nrealloc(matches, (*num_matches + 1) * 2230 sizeof(char *)); 2231 matches[*num_matches] = mallocstrcpy(NULL, nextdir->d_name); 2232 ++(*num_matches); 2233 } 2234 } 2235 2236 closedir(dir); 2237 free(dirname); 2238 free(filename); 2239 2240 return matches; 2241} 2242 2243/* Do tab completion. place refers to how much the statusbar cursor 2244 * position should be advanced. refresh_func is the function we will 2245 * call to refresh the edit window. */ 2246char *input_tab(char *buf, bool allow_files, size_t *place, bool 2247 *lastwastab, void (*refresh_func)(void), bool *list) 2248{ 2249 size_t num_matches = 0; 2250 char **matches = NULL; 2251 2252 assert(buf != NULL && place != NULL && *place <= strlen(buf) && lastwastab != NULL && refresh_func != NULL && list != NULL); 2253 2254 *list = FALSE; 2255 2256 /* If the word starts with `~' and there is no slash in the word, 2257 * then try completing this word as a username. */ 2258 if (*place > 0 && buf[0] == '~') { 2259 const char *bob = strchr(buf, '/'); 2260 2261 if (bob == NULL || bob >= buf + *place) 2262 matches = username_tab_completion(buf, &num_matches, 2263 *place); 2264 } 2265 2266 /* Match against files relative to the current working directory. */ 2267 if (matches == NULL) 2268 matches = cwd_tab_completion(buf, allow_files, &num_matches, 2269 *place); 2270 2271 if (num_matches == 0) 2272 beep(); 2273 else { 2274 size_t match, common_len = 0; 2275 char *mzero; 2276 const char *lastslash = revstrstr(buf, "/", buf + *place); 2277 size_t lastslash_len = (lastslash == NULL) ? 0 : 2278 lastslash - buf + 1; 2279 char *match1_mb = charalloc(mb_cur_max() + 1); 2280 char *match2_mb = charalloc(mb_cur_max() + 1); 2281 int match1_mb_len, match2_mb_len; 2282 2283 while (TRUE) { 2284 for (match = 1; match < num_matches; match++) { 2285 /* Get the number of single-byte characters that all the 2286 * matches have in common. */ 2287 match1_mb_len = parse_mbchar(matches[0] + common_len, 2288 match1_mb, NULL); 2289 match2_mb_len = parse_mbchar(matches[match] + 2290 common_len, match2_mb, NULL); 2291 match1_mb[match1_mb_len] = '\0'; 2292 match2_mb[match2_mb_len] = '\0'; 2293 if (strcmp(match1_mb, match2_mb) != 0) 2294 break; 2295 } 2296 2297 if (match < num_matches || matches[0][common_len] == '\0') 2298 break; 2299 2300 common_len += parse_mbchar(buf + common_len, NULL, NULL); 2301 } 2302 2303 free(match1_mb); 2304 free(match2_mb); 2305 2306 mzero = charalloc(lastslash_len + common_len + 1); 2307 2308 strncpy(mzero, buf, lastslash_len); 2309 strncpy(mzero + lastslash_len, matches[0], common_len); 2310 2311 common_len += lastslash_len; 2312 mzero[common_len] = '\0'; 2313 2314 assert(common_len >= *place); 2315 2316 if (num_matches == 1 && is_dir(mzero)) { 2317 mzero[common_len++] = '/'; 2318 2319 assert(common_len > *place); 2320 } 2321 2322 if (num_matches > 1 && (common_len != *place || !*lastwastab)) 2323 beep(); 2324 2325 /* If there is more of a match to display on the statusbar, show 2326 * it. We reset lastwastab to FALSE: it requires pressing Tab 2327 * twice in succession with no statusbar changes to see a match 2328 * list. */ 2329 if (common_len != *place) { 2330 size_t buf_len = strlen(buf); 2331 2332 *lastwastab = FALSE; 2333 buf = charealloc(buf, common_len + buf_len - *place + 1); 2334 charmove(buf + common_len, buf + *place, buf_len - 2335 *place + 1); 2336 strncpy(buf, mzero, common_len); 2337 *place = common_len; 2338 } else if (!*lastwastab || num_matches < 2) 2339 *lastwastab = TRUE; 2340 else { 2341 int longest_name = 0, columns, editline = 0; 2342 2343 /* Now we show a list of the available choices. */ 2344 assert(num_matches > 1); 2345 2346 /* Sort the list. */ 2347 qsort(matches, num_matches, sizeof(char *), diralphasort); 2348 2349 for (match = 0; match < num_matches; match++) { 2350 common_len = strnlenpt(matches[match], COLS - 1); 2351 2352 if (common_len > COLS - 1) { 2353 longest_name = COLS - 1; 2354 break; 2355 } 2356 2357 if (common_len > longest_name) 2358 longest_name = common_len; 2359 } 2360 2361 assert(longest_name <= COLS - 1); 2362 2363 /* Each column will be (longest_name + 2) columns wide, i.e. 2364 * two spaces between columns, except that there will be 2365 * only one space after the last column. */ 2366 columns = (COLS + 1) / (longest_name + 2); 2367 2368 /* Blank the edit window, and print the matches out 2369 * there. */ 2370 blank_edit(); 2371 wmove(edit, 0, 0); 2372 2373 /* Disable el cursor. */ 2374 curs_set(0); 2375 2376 for (match = 0; match < num_matches; match++) { 2377 char *disp; 2378 2379 wmove(edit, editline, (longest_name + 2) * 2380 (match % columns)); 2381 2382 if (match % columns == 0 && 2383 editline == editwinrows - 1 && 2384 num_matches - match > columns) { 2385 waddstr(edit, _("(more)")); 2386 break; 2387 } 2388 2389 disp = display_string(matches[match], 0, longest_name, 2390 FALSE); 2391 waddstr(edit, disp); 2392 free(disp); 2393 2394 if ((match + 1) % columns == 0) 2395 editline++; 2396 } 2397 2398 wnoutrefresh(edit); 2399 *list = TRUE; 2400 } 2401 2402 free(mzero); 2403 } 2404 2405 free_chararray(matches, num_matches); 2406 2407 /* Only refresh the edit window if we don't have a list of filename 2408 * matches on it. */ 2409 if (!*list) 2410 refresh_func(); 2411 2412 /* Enable el cursor. */ 2413 curs_set(1); 2414 2415 return buf; 2416} 2417#endif /* !DISABLE_TABCOMP */ 2418 2419/* Only print the last part of a path. Isn't there a shell command for 2420 * this? */ 2421const char *tail(const char *foo) 2422{ 2423 const char *tmp = strrchr(foo, '/'); 2424 2425 if (tmp == NULL) 2426 tmp = foo; 2427 else 2428 tmp++; 2429 2430 return tmp; 2431} 2432 2433#if !defined(NANO_TINY) && defined(ENABLE_NANORC) 2434/* Return $HOME/.nano_history, or NULL if we can't find the home 2435 * directory. The string is dynamically allocated, and should be 2436 * freed. */ 2437char *histfilename(void) 2438{ 2439 char *nanohist = NULL; 2440 2441 if (homedir != NULL) { 2442 size_t homelen = strlen(homedir); 2443 2444 nanohist = charalloc(homelen + 15); 2445 strcpy(nanohist, homedir); 2446 strcpy(nanohist + homelen, "/.nano_history"); 2447 } 2448 2449 return nanohist; 2450} 2451 2452/* Load histories from ~/.nano_history. */ 2453void load_history(void) 2454{ 2455 char *nanohist = histfilename(); 2456 2457 /* Assume do_rcfile() has reported a missing home directory. */ 2458 if (nanohist != NULL) { 2459 FILE *hist = fopen(nanohist, "rb"); 2460 2461 if (hist == NULL) { 2462 if (errno != ENOENT) { 2463 /* Don't save history when we quit. */ 2464 UNSET(HISTORYLOG); 2465 rcfile_error(N_("Error reading %s: %s"), nanohist, 2466 strerror(errno)); 2467 fprintf(stderr, 2468 _("\nPress Enter to continue starting nano.\n")); 2469 while (getchar() != '\n') 2470 ; 2471 } 2472 } else { 2473 /* Load a history list (first the search history, then the 2474 * replace history) from the oldest entry to the newest. 2475 * Assume the last history entry is a blank line. */ 2476 filestruct **history = &search_history; 2477 char *line = NULL; 2478 size_t buflen = 0; 2479 ssize_t read; 2480 2481 while ((read = getline(&line, &buflen, hist)) >= 0) { 2482 if (read > 0 && line[read - 1] == '\n') { 2483 read--; 2484 line[read] = '\0'; 2485 } 2486 if (read > 0) { 2487 unsunder(line, read); 2488 update_history(history, line); 2489 } else 2490 history = &replace_history; 2491 } 2492 2493 fclose(hist); 2494 free(line); 2495 } 2496 free(nanohist); 2497 } 2498} 2499 2500/* Write the lines of a history list, starting with the line at h, to 2501 * the open file at hist. Return TRUE if the write succeeded, and FALSE 2502 * otherwise. */ 2503bool writehist(FILE *hist, filestruct *h) 2504{ 2505 filestruct *p; 2506 2507 /* Write a history list from the oldest entry to the newest. Assume 2508 * the last history entry is a blank line. */ 2509 for (p = h; p != NULL; p = p->next) { 2510 size_t p_len = strlen(p->data); 2511 2512 sunder(p->data); 2513 2514 if (fwrite(p->data, sizeof(char), p_len, hist) < p_len || 2515 putc('\n', hist) == EOF) 2516 return FALSE; 2517 } 2518 2519 return TRUE; 2520} 2521 2522/* Save histories to ~/.nano_history. */ 2523void save_history(void) 2524{ 2525 char *nanohist; 2526 2527 /* Don't save unchanged or empty histories. */ 2528 if (!history_has_changed() || (searchbot->lineno == 1 && 2529 replacebot->lineno == 1)) 2530 return; 2531 2532 nanohist = histfilename(); 2533 2534 if (nanohist != NULL) { 2535 FILE *hist = fopen(nanohist, "wb"); 2536 2537 if (hist == NULL) 2538 rcfile_error(N_("Error writing %s: %s"), nanohist, 2539 strerror(errno)); 2540 else { 2541 /* Make sure no one else can read from or write to the 2542 * history file. */ 2543 chmod(nanohist, S_IRUSR | S_IWUSR); 2544 2545 if (!writehist(hist, searchage) || !writehist(hist, 2546 replaceage)) 2547 rcfile_error(N_("Error writing %s: %s"), nanohist, 2548 strerror(errno)); 2549 2550 fclose(hist); 2551 } 2552 2553 free(nanohist); 2554 } 2555} 2556#endif /* !NANO_TINY && ENABLE_NANORC */ 2557