files.c (79799B)
1 /************************************************************************** 2 * files.c -- This file is part of GNU nano. * 3 * * 4 * Copyright (C) 1999-2011, 2013-2025 Free Software Foundation, Inc. * 5 * Copyright (C) 2015-2022 Benno Schulenberg * 6 * * 7 * GNU nano is free software: you can redistribute it and/or modify * 8 * it under the terms of the GNU General Public License as published * 9 * by the Free Software Foundation, either version 3 of the License, * 10 * or (at your option) any later version. * 11 * * 12 * GNU nano is distributed in the hope that it will be useful, * 13 * but WITHOUT ANY WARRANTY; without even the implied warranty * 14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * 15 * See the GNU 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, see https://gnu.org/licenses/. * 19 * * 20 **************************************************************************/ 21 22 #include "prototypes.h" 23 24 #include <errno.h> 25 #include <fcntl.h> 26 #include <libgen.h> 27 #ifdef HAVE_PWD_H 28 #include <pwd.h> 29 #endif 30 #include <string.h> 31 #include <unistd.h> 32 #include <sys/wait.h> 33 34 #define RW_FOR_ALL (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH) 35 36 #ifndef HAVE_FSYNC 37 # define fsync(...) 0 38 #endif 39 40 /* Add an item to the circular list of openfile structs. */ 41 void make_new_buffer(void) 42 { 43 openfilestruct *newnode = nmalloc(sizeof(openfilestruct)); 44 45 #ifdef ENABLE_MULTIBUFFER 46 if (openfile == NULL) { 47 /* Make the first buffer the only element in the list. */ 48 newnode->prev = newnode; 49 newnode->next = newnode; 50 51 startfile = newnode; 52 } else { 53 /* Add the new buffer after the current one in the list. */ 54 newnode->prev = openfile; 55 newnode->next = openfile->next; 56 openfile->next->prev = newnode; 57 openfile->next = newnode; 58 59 /* There is more than one buffer: show "Close" in help lines. */ 60 exitfunc->tag = close_tag; 61 more_than_one = !inhelp || more_than_one; 62 } 63 #endif 64 /* Make the new buffer the current one, and start initializing it. */ 65 openfile = newnode; 66 67 openfile->filename = copy_of(""); 68 69 openfile->filetop = make_new_node(NULL); 70 openfile->filetop->data = copy_of(""); 71 openfile->filebot = openfile->filetop; 72 73 openfile->current = openfile->filetop; 74 openfile->current_x = 0; 75 openfile->placewewant = 0; 76 openfile->cursor_row = 0; 77 78 openfile->edittop = openfile->filetop; 79 openfile->firstcolumn = 0; 80 81 openfile->totsize = 0; 82 openfile->modified = FALSE; 83 #ifdef ENABLE_WRAPPING 84 openfile->spillage_line = NULL; 85 #endif 86 #ifndef NANO_TINY 87 openfile->mark = NULL; 88 openfile->softmark = FALSE; 89 90 openfile->fmt = UNSPECIFIED; 91 92 openfile->undotop = NULL; 93 openfile->current_undo = NULL; 94 openfile->last_saved = NULL; 95 openfile->last_action = OTHER; 96 97 openfile->statinfo = NULL; 98 openfile->lock_filename = NULL; 99 #endif 100 #ifdef ENABLE_MULTIBUFFER 101 openfile->errormessage = NULL; 102 #endif 103 #ifdef ENABLE_COLOR 104 openfile->syntax = NULL; 105 #endif 106 } 107 108 /* Return the given file name in a way that fits within the given space. */ 109 char *crop_to_fit(const char *name, int room) 110 { 111 char *clipped; 112 113 if (breadth(name) <= room) 114 return display_string(name, 0, room, FALSE, FALSE); 115 116 if (room < 4) 117 return copy_of("_"); 118 119 clipped = display_string(name, breadth(name) - room + 3, room, FALSE, FALSE); 120 121 clipped = nrealloc(clipped, strlen(clipped) + 4); 122 memmove(clipped + 3, clipped, strlen(clipped) + 1); 123 clipped[0] = '.'; clipped[1] = '.'; clipped[2] = '.'; 124 125 return clipped; 126 } 127 128 #ifndef NANO_TINY 129 /* Delete the lock file. Return TRUE on success, and FALSE otherwise. */ 130 bool delete_lockfile(const char *lockfilename) 131 { 132 if (unlink(lockfilename) < 0 && errno != ENOENT) { 133 statusline(MILD, _("Error deleting lock file %s: %s"), 134 lockfilename, strerror(errno)); 135 return FALSE; 136 } else 137 return TRUE; 138 } 139 140 #define LOCKSIZE 1024 141 #define SKIPTHISFILE (char *)-1 142 143 const char *locking_prefix = "."; 144 const char *locking_suffix = ".swp"; 145 146 /* Write a lock file, under the given lockfilename. This always annihilates an 147 * existing version of that file. Return TRUE on success; FALSE otherwise. */ 148 bool write_lockfile(const char *lockfilename, const char *filename, bool modified) 149 { 150 #if defined(HAVE_PWD_H) && defined(HAVE_GETEUID) 151 pid_t mypid = getpid(); 152 uid_t myuid = geteuid(); 153 struct passwd *mypwuid = getpwuid(myuid); 154 char myhostname[32]; 155 int fd; 156 FILE *filestream = NULL; 157 char *lockdata; 158 size_t wroteamt; 159 160 if (mypwuid == NULL) { 161 /* TRANSLATORS: Keep the next seven messages at most 76 characters. */ 162 statusline(MILD, _("Couldn't determine my identity for lock file")); 163 return FALSE; 164 } 165 166 if (gethostname(myhostname, 31) < 0 && errno != ENAMETOOLONG) { 167 statusline(MILD, _("Couldn't determine hostname: %s"), strerror(errno)); 168 return FALSE; 169 } else 170 myhostname[31] = '\0'; 171 172 /* First make sure to remove an existing lock file. */ 173 if (!delete_lockfile(lockfilename)) 174 return FALSE; 175 176 /* Create the lock file -- do not accept an existing one. */ 177 fd = open(lockfilename, O_WRONLY|O_CREAT|O_EXCL, RW_FOR_ALL); 178 179 if (fd > 0) 180 filestream = fdopen(fd, "wb"); 181 182 if (filestream == NULL) { 183 statusline(MILD, _("Error writing lock file %s: %s"), 184 lockfilename, strerror(errno)); 185 if (fd > 0) 186 close(fd); 187 return FALSE; 188 } 189 190 lockdata = nmalloc(LOCKSIZE); 191 memset(lockdata, 0, LOCKSIZE); 192 193 /* This is the lock data we will store (other bytes remain 0x00): 194 * 195 * bytes 0-1 - 0x62 0x30 196 * bytes 2-11 - name of program that created the lock 197 * bytes 24-27 - PID (little endian) of creator process 198 * bytes 28-43 - username of the user who created the lock 199 * bytes 68-99 - hostname of machine from where the lock was created 200 * bytes 108-876 - filename that the lock is for 201 * byte 1007 - 0x55 if file is modified 202 * 203 * Nano does not write the page size (bytes 12-15), nor the modification 204 * time (bytes 16-19), nor the inode of the relevant file (bytes 20-23). 205 * Nano also does not use all available space for user name (40 bytes), 206 * host name (40 bytes), and file name (890 bytes). Nor does nano write 207 * some byte-order-checking numbers (bytes 1008-1022). */ 208 lockdata[0] = 0x62; 209 lockdata[1] = 0x30; 210 /* It's fine to overwrite byte 12 with the \0 as it is 0x00 anyway. */ 211 snprintf(&lockdata[2], 11, "nano %s", VERSION); 212 lockdata[24] = mypid % 256; 213 lockdata[25] = (mypid / 256) % 256; 214 lockdata[26] = (mypid / (256 * 256)) % 256; 215 lockdata[27] = mypid / (256 * 256 * 256); 216 strncpy(&lockdata[28], mypwuid->pw_name, 16); 217 strncpy(&lockdata[68], myhostname, 32); 218 strncpy(&lockdata[108], filename, 768); 219 lockdata[1007] = (modified) ? 0x55 : 0x00; 220 221 wroteamt = fwrite(lockdata, 1, LOCKSIZE, filestream); 222 223 free(lockdata); 224 225 if (fclose(filestream) == EOF || wroteamt < LOCKSIZE) { 226 statusline(MILD, _("Error writing lock file %s: %s"), 227 lockfilename, strerror(errno)); 228 return FALSE; 229 } 230 #endif 231 return TRUE; 232 } 233 234 /* First check if a lock file already exists. If so, and ask_the_user is TRUE, 235 * then ask whether to open the corresponding file anyway. Return SKIPTHISFILE 236 * when the user answers "No", return the name of the lock file on success, and 237 * return NULL on failure. */ 238 char *do_lockfile(const char *filename, bool ask_the_user) 239 { 240 char *namecopy = copy_of(filename); 241 char *secondcopy = copy_of(filename); 242 size_t locknamesize = strlen(filename) + strlen(locking_prefix) + 243 strlen(locking_suffix) + 3; 244 char *lockfilename = nmalloc(locknamesize); 245 struct stat fileinfo; 246 247 snprintf(lockfilename, locknamesize, "%s/%s%s%s", dirname(namecopy), 248 locking_prefix, basename(secondcopy), locking_suffix); 249 free(secondcopy); 250 free(namecopy); 251 252 if (!ask_the_user && stat(lockfilename, &fileinfo) != -1) { 253 blank_bottombars(); 254 statusline(ALERT, _("Someone else is also editing this file")); 255 napms(1200); 256 } else if (stat(lockfilename, &fileinfo) != -1) { 257 char *lockbuf, *question, *pidstring, *postedname, *promptstr; 258 static char lockprog[11], lockuser[17]; 259 int lockfd, lockpid, choice; 260 ssize_t readamt; 261 262 if ((lockfd = open(lockfilename, O_RDONLY)) < 0) { 263 statusline(ALERT, _("Error opening lock file %s: %s"), 264 lockfilename, strerror(errno)); 265 free(lockfilename); 266 return NULL; 267 } 268 269 lockbuf = nmalloc(LOCKSIZE); 270 271 readamt = read(lockfd, lockbuf, LOCKSIZE); 272 273 close(lockfd); 274 275 /* If not enough data has been read to show the needed things, 276 * or the two magic bytes are not there, skip the lock file. */ 277 if (readamt < 68 || lockbuf[0] != 0x62 || lockbuf[1] != 0x30) { 278 statusline(ALERT, _("Bad lock file is ignored: %s"), lockfilename); 279 free(lockfilename); 280 free(lockbuf); 281 return NULL; 282 } 283 284 strncpy(lockprog, &lockbuf[2], 10); 285 lockprog[10] = '\0'; 286 lockpid = (((unsigned char)lockbuf[27] * 256 + (unsigned char)lockbuf[26]) * 256 + 287 (unsigned char)lockbuf[25]) * 256 + (unsigned char)lockbuf[24]; 288 strncpy(lockuser, &lockbuf[28], 16); 289 lockuser[16] = '\0'; 290 free(lockbuf); 291 292 pidstring = nmalloc(11); 293 sprintf (pidstring, "%u", (unsigned int)lockpid); 294 295 /* Display newlines in filenames as ^J. */ 296 as_an_at = FALSE; 297 298 /* TRANSLATORS: The second %s is the name of the user, the third that of the editor. */ 299 question = _("File %s is being edited by %s (with %s, PID %s); open anyway?"); 300 postedname = crop_to_fit(filename, COLS - breadth(question) - breadth(lockuser) - 301 breadth(lockprog) - breadth(pidstring) + 7); 302 303 /* Allow extra space for username (14), program name (8), PID (8), 304 * and terminating \0 (1), minus the %s (2) for the file name. */ 305 promptstr = nmalloc(strlen(question) + 29 + strlen(postedname)); 306 sprintf(promptstr, question, postedname, lockuser, lockprog, pidstring); 307 free(postedname); 308 free(pidstring); 309 310 choice = ask_user(YESORNO, promptstr); 311 free(promptstr); 312 313 /* When the user cancelled while we're still starting up, quit. */ 314 if (choice == CANCEL && !we_are_running) 315 finish(); 316 317 if (choice != YES) { 318 free(lockfilename); 319 wipe_statusbar(); 320 return SKIPTHISFILE; 321 } 322 } 323 324 if (write_lockfile(lockfilename, filename, FALSE)) 325 return lockfilename; 326 327 free(lockfilename); 328 return NULL; 329 } 330 331 /* Perform a stat call on the given filename, allocating a stat struct 332 * if necessary. On success, *pstat points to the stat's result. On 333 * failure, *pstat is freed and made NULL. */ 334 void stat_with_alloc(const char *filename, struct stat **pstat) 335 { 336 if (*pstat == NULL) 337 *pstat = nmalloc(sizeof(struct stat)); 338 339 if (stat(filename, *pstat) != 0) { 340 free(*pstat); 341 *pstat = NULL; 342 } 343 } 344 #endif /* !NANO_TINY */ 345 346 /* Verify that the containing directory of the given filename exists. */ 347 bool has_valid_path(const char *filename) 348 { 349 char *namecopy = copy_of(filename); 350 char *parentdir = dirname(namecopy); 351 struct stat parentinfo; 352 bool validity = FALSE; 353 bool gone = FALSE; 354 355 if (strcmp(parentdir, ".") == 0) { 356 char *currentdir = realpath(".", NULL); 357 358 gone = (currentdir == NULL && errno == ENOENT); 359 free(currentdir); 360 } 361 362 if (gone) 363 statusline(ALERT, _("The working directory has disappeared")); 364 else if (stat(parentdir, &parentinfo) == -1) { 365 if (errno == ENOENT) 366 /* TRANSLATORS: Keep the next ten messages at most 76 characters. */ 367 statusline(ALERT, _("Directory '%s' does not exist"), parentdir); 368 else 369 statusline(ALERT, _("Path '%s': %s"), parentdir, strerror(errno)); 370 } else if (!S_ISDIR(parentinfo.st_mode)) 371 statusline(ALERT, _("Path '%s' is not a directory"), parentdir); 372 else if (access(parentdir, X_OK) == -1) 373 statusline(ALERT, _("Path '%s' is not accessible"), parentdir); 374 #ifndef NANO_TINY 375 else if (ISSET(LOCKING) && !ISSET(VIEW_MODE) && access(parentdir, W_OK) < 0) 376 statusline(MILD, _("Directory '%s' is not writable"), parentdir); 377 #endif 378 else 379 validity = TRUE; 380 381 free(namecopy); 382 383 return validity; 384 } 385 386 /* This does one of three things. If the filename is "", it just creates 387 * a new empty buffer. When the filename is not empty, it reads that file 388 * into a new buffer when requested, otherwise into the existing buffer. */ 389 bool open_buffer(const char *filename, bool new_one) 390 { 391 char *realname; 392 /* The filename after tilde expansion. */ 393 struct stat fileinfo; 394 int descriptor = 0; 395 /* Code 0 means new file, -1 means failure, and else it's the fd. */ 396 FILE *f; 397 398 /* Display newlines in filenames as ^J. */ 399 as_an_at = FALSE; 400 401 #ifdef ENABLE_OPERATINGDIR 402 if (outside_of_confinement(filename, FALSE)) { 403 statusline(ALERT, _("Can't read file from outside of %s"), operating_dir); 404 return FALSE; 405 } 406 #endif 407 408 realname = real_dir_from_tilde(filename); 409 410 /* Don't try to open directories, character files, or block files. */ 411 if (*filename != '\0' && stat(realname, &fileinfo) == 0) { 412 if (S_ISDIR(fileinfo.st_mode)) { 413 statusline(ALERT, _("\"%s\" is a directory"), realname); 414 free(realname); 415 return FALSE; 416 } 417 if (S_ISCHR(fileinfo.st_mode) || S_ISBLK(fileinfo.st_mode)) { 418 statusline(ALERT, _("\"%s\" is a device file"), realname); 419 free(realname); 420 return FALSE; 421 } 422 #ifdef NANO_TINY 423 if (S_ISFIFO(fileinfo.st_mode)) { 424 statusline(ALERT, _("\"%s\" is a FIFO"), realname); 425 free(realname); 426 return FALSE; 427 } 428 #elif defined(HAVE_GETEUID) 429 if (new_one && !(fileinfo.st_mode & (S_IWUSR|S_IWGRP|S_IWOTH)) && 430 geteuid() == ROOT_UID) 431 statusline(ALERT, _("%s is meant to be read-only"), realname); 432 #endif 433 } 434 435 /* When loading into a new buffer, first check the file's path is valid, 436 * and then (if requested and possible) create a lock file for it. */ 437 if (new_one) { 438 make_new_buffer(); 439 440 if (has_valid_path(realname)) { 441 #ifndef NANO_TINY 442 if (ISSET(LOCKING) && !ISSET(VIEW_MODE) && filename[0] != '\0') { 443 char *thelocksname = do_lockfile(realname, TRUE); 444 445 /* When not overriding an existing lock, discard the buffer. */ 446 if (thelocksname == SKIPTHISFILE) { 447 #ifdef ENABLE_MULTIBUFFER 448 close_buffer(); 449 #endif 450 free(realname); 451 return FALSE; 452 } else 453 openfile->lock_filename = thelocksname; 454 } 455 #endif /* NANO_TINY */ 456 } 457 } 458 459 /* If we have a filename and are not in NOREAD mode, open the file. */ 460 if (filename[0] != '\0' && !ISSET(NOREAD_MODE)) 461 descriptor = open_file(realname, new_one, &f); 462 463 /* If we've successfully opened an existing file, read it in. */ 464 if (descriptor > 0) { 465 install_handler_for_Ctrl_C(); 466 467 read_file(f, descriptor, realname, !new_one); 468 469 restore_handler_for_Ctrl_C(); 470 471 #ifndef NANO_TINY 472 if (openfile->statinfo == NULL) 473 stat_with_alloc(realname, &openfile->statinfo); 474 #endif 475 } 476 477 /* For a new buffer, store filename and put cursor at start of buffer. */ 478 if (descriptor >= 0 && new_one) { 479 openfile->filename = mallocstrcpy(openfile->filename, realname); 480 openfile->current = openfile->filetop; 481 openfile->current_x = 0; 482 openfile->placewewant = 0; 483 } 484 485 #ifdef ENABLE_COLOR 486 /* If a new buffer was opened, check whether a syntax can be applied. */ 487 if (new_one) 488 find_and_prime_applicable_syntax(); 489 #endif 490 491 free(realname); 492 return TRUE; 493 } 494 495 /* Mark the current buffer as modified if it isn't already, and 496 * then update the title bar to display the buffer's new status. */ 497 void set_modified(void) 498 { 499 if (openfile->modified) 500 return; 501 502 openfile->modified = TRUE; 503 titlebar(NULL); 504 505 #ifndef NANO_TINY 506 if (openfile->lock_filename != NULL) 507 write_lockfile(openfile->lock_filename, openfile->filename, TRUE); 508 #endif 509 } 510 511 /* Update the title bar and the multiline cache to match the current buffer. */ 512 void prepare_for_display(void) 513 { 514 /* Update the title bar, since the filename may have changed. */ 515 if (!inhelp) 516 titlebar(NULL); 517 518 #ifdef ENABLE_COLOR 519 /* Precalculate the data for any multiline coloring regexes. */ 520 if (!openfile->filetop->multidata) 521 precalc_multicolorinfo(); 522 have_palette = FALSE; 523 #endif 524 refresh_needed = TRUE; 525 } 526 527 #ifdef ENABLE_MULTIBUFFER 528 /* Show name of current buffer and its number of lines on the status bar. */ 529 void mention_name_and_linecount(void) 530 { 531 size_t count = openfile->filebot->lineno - 532 (openfile->filebot->data[0] == '\0' ? 1 : 0); 533 534 #ifndef NANO_TINY 535 if (ISSET(MINIBAR)) { 536 report_size = TRUE; 537 return; 538 } else if (ISSET(ZERO)) 539 return; 540 541 if (openfile->fmt > NIX_FILE) 542 /* TRANSLATORS: First %s is file name, second %s is file format. */ 543 statusline(HUSH, P_("%s -- %zu line (%s)", "%s -- %zu lines (%s)", count), 544 openfile->filename[0] == '\0' ? 545 _("New Buffer") : tail(openfile->filename), count, 546 openfile->fmt == DOS_FILE ? _("DOS") : _("Mac")); 547 else 548 #endif 549 statusline(HUSH, P_("%s -- %zu line", "%s -- %zu lines", count), 550 openfile->filename[0] == '\0' ? 551 _("New Buffer") : tail(openfile->filename), count); 552 } 553 554 /* Update title bar and such after switching to another buffer.*/ 555 void redecorate_after_switch(void) 556 { 557 /* If only one file buffer is open, there is nothing to update. */ 558 if (openfile == openfile->next) { 559 statusline(AHEM, _("No more open file buffers")); 560 return; 561 } 562 563 #ifndef NANO_TINY 564 /* While in a different buffer, the width of the screen may have changed, 565 * so make sure that the starting column for the first row is fitting. */ 566 ensure_firstcolumn_is_aligned(); 567 #endif 568 569 /* Update title bar and multiline info to match the current buffer. */ 570 prepare_for_display(); 571 572 /* Ensure that the main loop will redraw the help lines. */ 573 currmenu = MMOST; 574 575 /* Prevent a possible Shift selection from getting cancelled. */ 576 shift_held = TRUE; 577 578 /* If the switched-to buffer gave an error during opening, show the message 579 * once; otherwise, indicate on the status bar which file we switched to. */ 580 if (openfile->errormessage) { 581 statusline(ALERT, openfile->errormessage); 582 free(openfile->errormessage); 583 openfile->errormessage = NULL; 584 } else 585 mention_name_and_linecount(); 586 } 587 588 /* Switch to the previous entry in the circular list of buffers. */ 589 void switch_to_prev_buffer(void) 590 { 591 openfile = openfile->prev; 592 redecorate_after_switch(); 593 } 594 595 /* Switch to the next entry in the circular list of buffers. */ 596 void switch_to_next_buffer(void) 597 { 598 openfile = openfile->next; 599 redecorate_after_switch(); 600 } 601 602 /* Remove the current buffer from the circular list of buffers. */ 603 void close_buffer(void) 604 { 605 openfilestruct *orphan = openfile; 606 607 if (orphan == startfile) 608 startfile = startfile->next; 609 610 orphan->prev->next = orphan->next; 611 orphan->next->prev = orphan->prev; 612 613 free(orphan->filename); 614 free_lines(orphan->filetop); 615 #ifndef NANO_TINY 616 free(orphan->statinfo); 617 free(orphan->lock_filename); 618 /* Free the undo stack. */ 619 discard_until(NULL); 620 #endif 621 free(orphan->errormessage); 622 623 openfile = orphan->prev; 624 if (openfile == orphan) 625 openfile = NULL; 626 627 free(orphan); 628 629 /* When just one buffer remains open, show "Exit" in the help lines. */ 630 if (openfile && openfile == openfile->next) 631 exitfunc->tag = exit_tag; 632 } 633 #endif /* ENABLE_MULTIBUFFER */ 634 635 /* Encode any NUL bytes in the given line of text (of the given length), 636 * and return a dynamically allocated copy of the resultant string. */ 637 char *encode_data(char *text, size_t length) 638 { 639 recode_NUL_to_LF(text, length); 640 text[length] = '\0'; 641 642 return copy_of(text); 643 } 644 645 /* The number of bytes by which we expand the line buffer while reading. */ 646 #define LUMPSIZE 120 647 648 /* Read the given open file f into the current buffer. filename should be 649 * set to the name of the file. undoable means that undo records should be 650 * created and that the file does not need to be checked for writability. */ 651 void read_file(FILE *f, int fd, const char *filename, bool undoable) 652 { 653 ssize_t was_lineno = openfile->current->lineno; 654 /* The line number where we start the insertion. */ 655 size_t was_leftedge = 0; 656 /* The leftedge where we start the insertion. */ 657 size_t num_lines = 0; 658 /* The number of lines in the file. */ 659 size_t len = 0; 660 /* The length of the current line of the file. */ 661 size_t bufsize = LUMPSIZE; 662 /* The size of the line buffer; increased as needed. */ 663 char *buf = nmalloc(bufsize); 664 /* The buffer in which we assemble each line of the file. */ 665 linestruct *topline; 666 /* The top of the new buffer where we store the read file. */ 667 linestruct *bottomline; 668 /* The bottom of the new buffer. */ 669 int onevalue; 670 /* The current value we read from the file, either a byte or EOF. */ 671 int errornumber; 672 /* The error code, in case an error occurred during reading. */ 673 bool writable = TRUE; 674 /* Whether the file is writable (in case we care). */ 675 #ifndef NANO_TINY 676 format_type format = NIX_FILE; 677 /* The type of line ending the file uses: Unix, DOS, or Mac. */ 678 679 if (undoable) 680 add_undo(INSERT, NULL); 681 682 if (ISSET(SOFTWRAP)) 683 was_leftedge = leftedge_for(xplustabs(), openfile->current); 684 #endif 685 686 /* Create an empty buffer. */ 687 topline = make_new_node(NULL); 688 bottomline = topline; 689 690 #ifndef NANO_TINY 691 block_sigwinch(TRUE); 692 #endif 693 694 #ifdef HAVE_FLOCKFILE 695 /* Lock the file before starting to read it, to avoid the overhead 696 * of locking it for each single byte that we read from it. */ 697 flockfile(f); 698 #else 699 # define getc_unlocked getc 700 #endif 701 702 control_C_was_pressed = FALSE; 703 704 /* Read in the entire file, byte by byte, line by line. */ 705 while ((onevalue = getc_unlocked(f)) != EOF) { 706 char input = (char)onevalue; 707 708 if (control_C_was_pressed) 709 break; 710 711 /* When the byte before the current one is a CR and automatic format 712 * conversion has not been switched off, then strip this CR when it's 713 * before a LF OR when the file is in Mac format. Also, when this is 714 * the first line break, make a note of the format. */ 715 if (input == '\n') { 716 #ifndef NANO_TINY 717 if (len > 0 && buf[len - 1] == '\r' && !ISSET(NO_CONVERT)) { 718 if (num_lines == 0) 719 format = DOS_FILE; 720 len--; 721 } 722 } else if ((num_lines == 0 || format == MAC_FILE) && 723 len > 0 && buf[len - 1] == '\r' && !ISSET(NO_CONVERT)) { 724 format = MAC_FILE; 725 len--; 726 #endif 727 } else { 728 /* Store the byte. */ 729 buf[len] = input; 730 731 /* Keep track of the total length of the line. It might have 732 * NUL bytes in it, so we can't just use strlen() later. */ 733 len++; 734 735 /* When needed, increase the line-buffer size. Don't bother 736 * decreasing it -- it gets freed when reading is finished. */ 737 if (len == bufsize) { 738 bufsize += LUMPSIZE; 739 buf = nrealloc(buf, bufsize); 740 } 741 742 continue; 743 } 744 745 /* Store the data and make a new line. */ 746 bottomline->data = encode_data(buf, len); 747 bottomline->next = make_new_node(bottomline); 748 bottomline = bottomline->next; 749 num_lines++; 750 751 /* Reset the length in preparation for the next line. */ 752 len = 0; 753 754 #ifndef NANO_TINY 755 /* If it was a Mac line, then store the byte after the \r 756 * as the first byte of the next line. */ 757 if (input != '\n') 758 buf[len++] = input; 759 #endif 760 } 761 762 errornumber = errno; 763 764 #ifdef HAVE_FUNLOCKFILE 765 /* We are done with the file, unlock it. */ 766 funlockfile(f); 767 #endif 768 769 #ifndef NANO_TINY 770 block_sigwinch(FALSE); 771 772 /* When reading from stdin, restore the terminal and reenter curses mode. */ 773 if (isendwin()) { 774 if (!isatty(STDIN_FILENO)) 775 reconnect_and_store_state(); 776 terminal_init(); 777 doupdate(); 778 } 779 #endif 780 781 /* If there was a real error during the reading, let the user know. */ 782 if (ferror(f) && errornumber != EINTR && errornumber != 0) 783 statusline(ALERT, strerror(errornumber)); 784 785 if (control_C_was_pressed) 786 statusline(ALERT, _("Interrupted")); 787 788 fclose(f); 789 790 if (fd > 0 && !undoable && !ISSET(VIEW_MODE)) 791 writable = (access(filename, W_OK) == 0); 792 793 /* If the file ended with a newline, or it was entirely empty, make the 794 * last line blank. Otherwise, put the last read data in. */ 795 if (len == 0) 796 bottomline->data = copy_of(""); 797 else { 798 #ifndef NANO_TINY 799 bool mac_line_needs_newline = FALSE; 800 801 /* If the final character is a CR and file conversion isn't disabled, 802 * strip this CR and indicate that an extra blank line is needed. */ 803 if (buf[len - 1] == '\r' && !ISSET(NO_CONVERT)) { 804 if (num_lines == 0) 805 format = MAC_FILE; 806 buf[--len] = '\0'; 807 mac_line_needs_newline = TRUE; 808 } 809 #endif 810 /* Store the data of the final line. */ 811 bottomline->data = encode_data(buf, len); 812 num_lines++; 813 814 #ifndef NANO_TINY 815 if (mac_line_needs_newline) { 816 bottomline->next = make_new_node(bottomline); 817 bottomline = bottomline->next; 818 bottomline->data = copy_of(""); 819 } 820 #endif 821 } 822 823 free(buf); 824 825 /* Insert the just read buffer into the current one. */ 826 ingraft_buffer(topline); 827 828 /* Set the desired x position at the end of what was inserted. */ 829 openfile->placewewant = xplustabs(); 830 831 if (!writable) 832 statusline(ALERT, _("File '%s' is unwritable"), filename); 833 else if ((ISSET(ZERO) || ISSET(MINIBAR)) && !(we_are_running && undoable)) 834 ; /* No blurb for new buffers with --zero or --mini. */ 835 #ifndef NANO_TINY 836 else if (format == MAC_FILE) 837 /* TRANSLATORS: Keep the next three messages at most 78 characters. */ 838 statusline(REMARK, P_("Read %zu line (converted from Mac format)", 839 "Read %zu lines (converted from Mac format)", 840 num_lines), num_lines); 841 else if (format == DOS_FILE) 842 statusline(REMARK, P_("Read %zu line (converted from DOS format)", 843 "Read %zu lines (converted from DOS format)", 844 num_lines), num_lines); 845 #endif 846 else 847 statusline(REMARK, P_("Read %zu line", "Read %zu lines", 848 num_lines), num_lines); 849 850 report_size = TRUE; 851 852 /* If we inserted less than a screenful, don't center the cursor. */ 853 if (undoable && less_than_a_screenful(was_lineno, was_leftedge)) { 854 focusing = FALSE; 855 #ifdef ENABLE_COLOR 856 perturbed = TRUE; 857 } else if (undoable) { 858 recook = TRUE; 859 #endif 860 } 861 862 #ifndef NANO_TINY 863 if (undoable) 864 update_undo(INSERT); 865 866 if (ISSET(MAKE_IT_UNIX)) 867 openfile->fmt = NIX_FILE; 868 else if (openfile->fmt == UNSPECIFIED) 869 openfile->fmt = format; 870 #endif 871 } 872 873 /* Open the file with the given name. If the file does not exist, display 874 * "New File" if new_one is TRUE, and say "File not found" otherwise. 875 * Return 0 if we say "New File", -1 upon failure, and the obtained file 876 * descriptor otherwise. The opened filestream is returned in *f. */ 877 int open_file(const char *filename, bool new_one, FILE **f) 878 { 879 char *full_filename = get_full_path(filename); 880 struct stat fileinfo; 881 int fd; 882 883 /* If the absolute path is unusable (due to some component's permissions), 884 * try the given path instead (as it is probably relative). */ 885 if (full_filename == NULL || stat(full_filename, &fileinfo) == -1) 886 full_filename = mallocstrcpy(full_filename, filename); 887 888 if (stat(full_filename, &fileinfo) == -1) { 889 free(full_filename); 890 891 if (new_one) { 892 statusline(REMARK, _("New File")); 893 return 0; 894 } else { 895 statusline(ALERT, _("File \"%s\" not found"), filename); 896 return -1; 897 } 898 } 899 900 #ifndef NANO_TINY 901 if (S_ISFIFO(fileinfo.st_mode)) 902 statusbar(_("Reading from FIFO...")); 903 904 block_sigwinch(TRUE); 905 install_handler_for_Ctrl_C(); 906 #endif 907 908 /* Try opening the file. */ 909 fd = open(full_filename, O_RDONLY); 910 911 #ifndef NANO_TINY 912 restore_handler_for_Ctrl_C(); 913 block_sigwinch(FALSE); 914 #endif 915 916 if (fd == -1) { 917 if (errno == EINTR || errno == 0) 918 statusline(ALERT, _("Interrupted")); 919 else 920 statusline(ALERT, _("Error reading %s: %s"), filename, strerror(errno)); 921 } else { 922 /* The file is A-OK. Associate a stream with it. */ 923 *f = fdopen(fd, "rb"); 924 925 if (*f == NULL) { 926 statusline(ALERT, _("Error reading %s: %s"), filename, strerror(errno)); 927 close(fd); 928 fd = -1; 929 } else if (!ISSET(ZERO) || we_are_running) 930 statusbar(_("Reading...")); 931 } 932 933 free(full_filename); 934 935 return fd; 936 } 937 938 /* This function will return the name of the first available extension 939 * of a filename (starting with [name][suffix], then [name][suffix].1, 940 * etc.). Memory is allocated for the return value. If no writable 941 * extension exists, we return "". */ 942 char *get_next_filename(const char *name, const char *suffix) 943 { 944 size_t wholenamelen= strlen(name) + strlen(suffix); 945 unsigned long i = 0; 946 char *buf; 947 948 /* Reserve space for: the name plus the suffix plus a dot plus 949 * possibly five digits plus a null byte. */ 950 buf = nmalloc(wholenamelen + 7); 951 sprintf(buf, "%s%s", name, suffix); 952 953 while (TRUE) { 954 struct stat fs; 955 956 if (stat(buf, &fs) == -1) 957 return buf; 958 959 /* Limit the number of backup files to a hundred thousand. */ 960 if (++i == 100000) 961 break; 962 963 sprintf(buf + wholenamelen, ".%lu", i); 964 } 965 966 /* There is no possible save file: blank out the filename. */ 967 *buf = '\0'; 968 969 return buf; 970 } 971 972 #ifndef NANO_TINY 973 static pid_t pid_of_command = -1; 974 /* The PID of a forked process -- needed when wanting to abort it. */ 975 static pid_t pid_of_sender = -1; 976 /* The PID of the process that pipes data to the above process. */ 977 static bool should_pipe = FALSE; 978 /* Whether we are piping data to the external command. */ 979 980 /* Send an unconditional kill signal to the running external command. */ 981 void cancel_the_command(int signal) 982 { 983 #ifdef SIGKILL 984 if (pid_of_command > 0) 985 kill(pid_of_command, SIGKILL); 986 if (should_pipe && pid_of_sender > 0) 987 kill(pid_of_sender, SIGKILL); 988 #endif 989 } 990 991 /* Send the text that starts at the given line to file descriptor fd. */ 992 void send_data(const linestruct *line, int fd) 993 { 994 FILE *tube = fdopen(fd, "w"); 995 996 if (tube == NULL) 997 exit(4); 998 999 /* Send each line, except a final empty line. */ 1000 while (line != NULL && (line->next != NULL || line->data[0] != '\0')) { 1001 size_t length = recode_LF_to_NUL(line->data); 1002 1003 if (fwrite(line->data, 1, length, tube) < length) 1004 exit(5); 1005 1006 if (line->next && putc('\n', tube) == EOF) 1007 exit(6); 1008 1009 line = line->next; 1010 } 1011 1012 fclose(tube); 1013 } 1014 1015 /* Execute the given command in a shell. */ 1016 void execute_command(const char *command) 1017 { 1018 #if defined(HAVE_FORK) && defined(HAVE_PIPE) && defined(HAVE_WAITPID) 1019 int from_fd[2], to_fd[2]; 1020 /* The pipes through which text will be written and read. */ 1021 struct sigaction oldaction, newaction = {{0}}; 1022 /* Original and temporary handlers for SIGINT. */ 1023 ssize_t was_lineno = (openfile->mark ? 0 : openfile->current->lineno); 1024 int command_status, sender_status; 1025 FILE *stream; 1026 1027 should_pipe = (command[0] == '|'); 1028 1029 /* Create a pipe to read the command's output from, and, if needed, 1030 * a pipe to feed the command's input through. */ 1031 if (pipe(from_fd) == -1 || (should_pipe && pipe(to_fd) == -1)) { 1032 statusline(ALERT, _("Could not create pipe: %s"), strerror(errno)); 1033 return; 1034 } 1035 1036 /* Fork a child process to run the command in. */ 1037 if ((pid_of_command = fork()) == 0) { 1038 const char *theshell = getenv("SHELL"); 1039 1040 if (theshell == NULL) 1041 theshell = (char *)"/bin/sh"; 1042 1043 /* Child: close the unused read end of the output pipe. */ 1044 close(from_fd[0]); 1045 1046 /* Connect the write end of the output pipe to the process' output streams. */ 1047 if (dup2(from_fd[1], STDOUT_FILENO) < 0) 1048 exit(3); 1049 if (dup2(from_fd[1], STDERR_FILENO) < 0) 1050 exit(4); 1051 1052 /* If the parent sends text, connect the read end of the 1053 * feeding pipe to the child's input stream. */ 1054 if (should_pipe) { 1055 if (dup2(to_fd[0], STDIN_FILENO) < 0) 1056 exit(5); 1057 close(from_fd[1]); 1058 close(to_fd[1]); 1059 } 1060 1061 /* Run the given command inside the preferred shell. */ 1062 execl(theshell, tail(theshell), "-c", should_pipe ? &command[1] : command, NULL); 1063 1064 /* If the exec call returns, there was an error. */ 1065 exit(6); 1066 } 1067 1068 /* Parent: close the unused write end of the pipe. */ 1069 close(from_fd[1]); 1070 1071 if (pid_of_command == -1) { 1072 statusline(ALERT, _("Could not fork: %s"), strerror(errno)); 1073 close(from_fd[0]); 1074 return; 1075 } 1076 1077 statusbar(_("Executing...")); 1078 1079 /* If the command starts with "|", pipe buffer or region to the command. */ 1080 if (should_pipe) { 1081 linestruct *was_cutbuffer = cutbuffer; 1082 bool whole_buffer = FALSE; 1083 1084 cutbuffer = NULL; 1085 1086 #ifdef ENABLE_MULTIBUFFER 1087 if (ISSET(MULTIBUFFER)) { 1088 openfile = openfile->prev; 1089 if (openfile->mark) 1090 copy_marked_region(); 1091 else 1092 whole_buffer = TRUE; 1093 } else 1094 #endif 1095 { 1096 /* TRANSLATORS: This one goes with Undid/Redid messages. */ 1097 add_undo(COUPLE_BEGIN, N_("filtering")); 1098 if (openfile->mark == NULL) { 1099 openfile->current = openfile->filetop; 1100 openfile->current_x = 0; 1101 } 1102 add_undo(CUT, NULL); 1103 do_snip(openfile->mark != NULL, openfile->mark == NULL, FALSE); 1104 if (openfile->filetop->next == NULL) 1105 openfile->filetop->has_anchor = FALSE; 1106 update_undo(CUT); 1107 } 1108 1109 /* Create a separate process for piping the data to the command. */ 1110 if ((pid_of_sender = fork()) == 0) { 1111 send_data(whole_buffer ? openfile->filetop : cutbuffer, to_fd[1]); 1112 exit(0); 1113 } 1114 1115 if (pid_of_sender == -1) 1116 statusline(ALERT, _("Could not fork: %s"), strerror(errno)); 1117 1118 close(to_fd[0]); 1119 close(to_fd[1]); 1120 1121 #ifdef ENABLE_MULTIBUFFER 1122 if (ISSET(MULTIBUFFER)) 1123 openfile = openfile->next; 1124 #endif 1125 free_lines(cutbuffer); 1126 cutbuffer = was_cutbuffer; 1127 } 1128 1129 /* Re-enable interpretation of the special control keys so that we get 1130 * SIGINT when Ctrl-C is pressed. */ 1131 enable_kb_interrupt(); 1132 1133 /* Set up a signal handler so that ^C will terminate the forked process. */ 1134 newaction.sa_handler = cancel_the_command; 1135 newaction.sa_flags = 0; 1136 sigaction(SIGINT, &newaction, &oldaction); 1137 1138 stream = fdopen(from_fd[0], "rb"); 1139 if (stream == NULL) 1140 statusline(ALERT, _("Failed to open pipe: %s"), strerror(errno)); 1141 else 1142 read_file(stream, 0, "pipe", TRUE); 1143 1144 if (should_pipe && !ISSET(MULTIBUFFER)) { 1145 if (was_lineno) 1146 goto_line_posx(was_lineno, 0); 1147 add_undo(COUPLE_END, N_("filtering")); 1148 } 1149 1150 /* Wait for the external command (and possibly data sender) to terminate. */ 1151 waitpid(pid_of_command, &command_status, 0); 1152 if (should_pipe && pid_of_sender > 0) 1153 waitpid(pid_of_sender, &sender_status, 0); 1154 1155 /* If the command failed, show what the shell reported. */ 1156 if (WIFEXITED(command_status) == 0 || WEXITSTATUS(command_status)) 1157 statusline(ALERT, WIFSIGNALED(command_status) ? _("Cancelled") : 1158 _("Error: %s"), openfile->current->prev && 1159 strstr(openfile->current->prev->data, ": ") ? 1160 strstr(openfile->current->prev->data, ": ") + 2 : "---"); 1161 else if (should_pipe && pid_of_sender > 0 && 1162 (WIFEXITED(sender_status) == 0 || WEXITSTATUS(sender_status))) 1163 statusline(ALERT, _("Piping failed")); 1164 1165 /* If there was an error, undo and discard what the command did. */ 1166 if (lastmessage == ALERT) { 1167 do_undo(); 1168 discard_until(openfile->current_undo); 1169 } 1170 1171 /* Restore the original handler for SIGINT. */ 1172 sigaction(SIGINT, &oldaction, NULL); 1173 1174 /* Restore the terminal to its desired state, and disable 1175 * interpretation of the special control keys again. */ 1176 terminal_init(); 1177 #endif 1178 } 1179 #endif /* NANO_TINY */ 1180 1181 /* Insert a file into the current buffer (or into a new buffer). But when 1182 * execute is TRUE, run a command in the shell and insert its output into 1183 * the buffer, or just run one of the tools listed in the help lines. */ 1184 void insert_a_file_or(bool execute) 1185 { 1186 int response; 1187 const char *msg; 1188 char *given = copy_of(""); 1189 /* The last answer the user typed at the status-bar prompt. */ 1190 #ifdef ENABLE_MULTIBUFFER 1191 bool was_multibuffer = ISSET(MULTIBUFFER); 1192 #endif 1193 1194 /* Display newlines in filenames as ^J. */ 1195 as_an_at = FALSE; 1196 1197 /* Reset the flag that is set by the Spell Checker and Linter and such. */ 1198 ran_a_tool = FALSE; 1199 1200 #ifndef NANO_TINY 1201 /* If something was typed at the Execute prompt without being run, restore it. */ 1202 if (execute && *foretext) 1203 given = mallocstrcpy(given, foretext); 1204 #endif 1205 1206 while (TRUE) { 1207 #ifndef NANO_TINY 1208 if (execute) { 1209 #ifdef ENABLE_MULTIBUFFER 1210 if (ISSET(MULTIBUFFER)) 1211 /* TRANSLATORS: The next six messages are prompts. */ 1212 msg = _("Command to execute in new buffer"); 1213 else 1214 #endif 1215 msg = _("Command to execute"); 1216 } else 1217 #endif 1218 { 1219 #ifdef ENABLE_MULTIBUFFER 1220 if (ISSET(MULTIBUFFER)) 1221 #ifndef NANO_TINY 1222 if ISSET(NO_CONVERT) 1223 msg = _("File to read unconverted into new buffer [from %s]"); 1224 else 1225 #endif 1226 msg = _("File to read into new buffer [from %s]"); 1227 else 1228 #endif 1229 #ifndef NANO_TINY 1230 if ISSET(NO_CONVERT) 1231 msg = _("File to insert unconverted [from %s]"); 1232 else 1233 #endif 1234 msg = _("File to insert [from %s]"); 1235 } 1236 1237 present_path = mallocstrcpy(present_path, "./"); 1238 1239 response = do_prompt(execute ? MEXECUTE : MINSERTFILE, given, 1240 execute ? &execute_history : NULL, 1241 edit_refresh, msg, 1242 #ifdef ENABLE_OPERATINGDIR 1243 operating_dir != NULL ? operating_dir : 1244 #endif 1245 "./"); 1246 1247 /* If we're in multibuffer mode and the filename or command is 1248 * blank, open a new buffer instead of canceling. */ 1249 if (response == -1 || (response == -2 && !ISSET(MULTIBUFFER))) { 1250 statusbar(_("Cancelled")); 1251 break; 1252 } else { 1253 ssize_t was_current_lineno = openfile->current->lineno; 1254 size_t was_current_x = openfile->current_x; 1255 #if !defined(NANO_TINY) || defined(ENABLE_BROWSER) || defined(ENABLE_MULTIBUFFER) 1256 functionptrtype function = func_from_key(response); 1257 #endif 1258 given = mallocstrcpy(given, answer); 1259 1260 if (ran_a_tool) 1261 break; 1262 1263 #ifdef ENABLE_MULTIBUFFER 1264 if (function == flip_newbuffer) { 1265 /* Allow toggling only when not in view mode. */ 1266 if (!ISSET(VIEW_MODE)) 1267 TOGGLE(MULTIBUFFER); 1268 else 1269 beep(); 1270 continue; 1271 } 1272 #endif 1273 #ifndef NANO_TINY 1274 if (function == flip_convert) { 1275 TOGGLE(NO_CONVERT); 1276 continue; 1277 } 1278 if (function == flip_execute) { 1279 execute = !execute; 1280 continue; 1281 } 1282 if (function == flip_pipe) { 1283 add_or_remove_pipe_symbol_from_answer(); 1284 given = mallocstrcpy(given, answer); 1285 continue; 1286 } 1287 #endif 1288 #ifdef ENABLE_BROWSER 1289 if (function == to_files) { 1290 char *chosen = browse_in(answer); 1291 1292 /* If no file was chosen, go back to the prompt. */ 1293 if (chosen == NULL) 1294 continue; 1295 1296 free(answer); 1297 answer = chosen; 1298 response = 0; 1299 } 1300 #endif 1301 /* If we don't have a file yet, go back to the prompt. */ 1302 if (response != 0 && (!ISSET(MULTIBUFFER) || response != -2)) 1303 continue; 1304 1305 #ifndef NANO_TINY 1306 if (execute) { 1307 #ifdef ENABLE_MULTIBUFFER 1308 /* When in multibuffer mode, first open a blank buffer. */ 1309 if (ISSET(MULTIBUFFER)) 1310 open_buffer("", TRUE); 1311 #endif 1312 /* If the command is not empty, execute it and read its output 1313 * into the buffer, and add the command to the history list. */ 1314 if (*answer != '\0') { 1315 execute_command(answer); 1316 #ifdef ENABLE_HISTORIES 1317 update_history(&execute_history, answer, PRUNE_DUPLICATE); 1318 #endif 1319 } 1320 1321 #ifdef ENABLE_MULTIBUFFER 1322 /* If this is a new buffer, put the cursor at the top. */ 1323 if (ISSET(MULTIBUFFER)) { 1324 openfile->current = openfile->filetop; 1325 openfile->current_x = 0; 1326 openfile->placewewant = 0; 1327 1328 set_modified(); 1329 } 1330 #endif 1331 } else 1332 #endif /* !NANO_TINY */ 1333 { 1334 /* Make sure the specified path is tilde-expanded. */ 1335 answer = free_and_assign(answer, real_dir_from_tilde(answer)); 1336 1337 /* Read the file into a new buffer or into current buffer. */ 1338 open_buffer(answer, ISSET(MULTIBUFFER)); 1339 } 1340 1341 #ifdef ENABLE_MULTIBUFFER 1342 if (ISSET(MULTIBUFFER)) { 1343 #ifdef ENABLE_HISTORIES 1344 if (ISSET(POSITIONLOG)) { 1345 #ifndef NANO_TINY 1346 if (!execute) 1347 #endif 1348 restore_cursor_position_if_any(); 1349 } 1350 #endif 1351 /* Update title bar and color info for this new buffer. */ 1352 prepare_for_display(); 1353 } else 1354 #endif /* ENABLE_MULTIBUFFER */ 1355 { 1356 /* If the buffer actually changed, mark it as modified. */ 1357 if (openfile->current->lineno != was_current_lineno || 1358 openfile->current_x != was_current_x) 1359 set_modified(); 1360 1361 refresh_needed = TRUE; 1362 } 1363 1364 break; 1365 } 1366 } 1367 1368 free(given); 1369 1370 #ifdef ENABLE_MULTIBUFFER 1371 if (was_multibuffer) 1372 SET(MULTIBUFFER); 1373 else 1374 UNSET(MULTIBUFFER); 1375 #endif 1376 } 1377 1378 /* If the current mode of operation allows it, go insert a file. */ 1379 void do_insertfile(void) 1380 { 1381 if (!in_restricted_mode()) 1382 insert_a_file_or(FALSE); 1383 } 1384 1385 #ifndef NANO_TINY 1386 /* If the current mode of operation allows it, go prompt for a command. */ 1387 void do_execute(void) 1388 { 1389 if (!in_restricted_mode()) 1390 insert_a_file_or(TRUE); 1391 } 1392 #endif 1393 1394 /* For the given bare path (or path plus filename), return the canonical, 1395 * absolute path (plus filename) when the path exists, and NULL when not. */ 1396 char *get_full_path(const char *origpath) 1397 { 1398 char *untilded, *target, *slash; 1399 struct stat fileinfo; 1400 1401 if (origpath == NULL) 1402 return NULL; 1403 1404 untilded = real_dir_from_tilde(origpath); 1405 target = realpath(untilded, NULL); 1406 slash = strrchr(untilded, '/'); 1407 1408 /* If realpath() returned NULL, try without the last component, 1409 * as this can be a file that does not exist yet. */ 1410 if (!target && slash && slash[1]) { 1411 *slash = '\0'; 1412 target = realpath(untilded, NULL); 1413 1414 /* Upon success, re-add the last component of the original path. */ 1415 if (target) { 1416 target = nrealloc(target, strlen(target) + strlen(slash + 1) + 1); 1417 strcat(target, slash + 1); 1418 } 1419 } 1420 1421 /* Ensure that a non-apex directory path ends with a slash. */ 1422 if (target && target[1] && stat(target, &fileinfo) == 0 && 1423 S_ISDIR(fileinfo.st_mode)) { 1424 target = nrealloc(target, strlen(target) + 2); 1425 strcat(target, "/"); 1426 } 1427 1428 free(untilded); 1429 1430 return target; 1431 } 1432 1433 /* Check whether the given path refers to a directory that is writable. 1434 * Return the absolute form of the path on success, and NULL on failure. */ 1435 char *check_writable_directory(const char *path) 1436 { 1437 char *full_path = get_full_path(path); 1438 1439 if (full_path == NULL) 1440 return NULL; 1441 1442 if (full_path[strlen(full_path) - 1] != '/' || access(full_path, W_OK) != 0) { 1443 free(full_path); 1444 return NULL; 1445 } 1446 1447 return full_path; 1448 } 1449 1450 /* Create, safely, a temporary file in the standard temp directory. 1451 * On success, return the malloc()ed filename, plus the corresponding 1452 * file stream opened in read-write mode. On error, return NULL. */ 1453 char *safe_tempfile(FILE **stream) 1454 { 1455 const char *env_dir = getenv("TMPDIR"); 1456 char *tempdir = NULL, *tempfile_name = NULL; 1457 char *extension; 1458 int descriptor; 1459 1460 /* Get the absolute path for the first directory among $TMPDIR 1461 * and P_tmpdir that is writable, otherwise use /tmp/. */ 1462 if (env_dir != NULL) 1463 tempdir = check_writable_directory(env_dir); 1464 1465 if (tempdir == NULL) 1466 tempdir = check_writable_directory(P_tmpdir); 1467 1468 if (tempdir == NULL) 1469 tempdir = copy_of("/tmp/"); 1470 1471 extension = strrchr(openfile->filename, '.'); 1472 1473 if (!extension || strchr(extension, '/')) 1474 extension = openfile->filename + strlen(openfile->filename); 1475 1476 tempfile_name = nrealloc(tempdir, strlen(tempdir) + 12 + strlen(extension)); 1477 strcat(tempfile_name, "nano.XXXXXX"); 1478 strcat(tempfile_name, extension); 1479 1480 descriptor = mkstemps(tempfile_name, strlen(extension)); 1481 1482 *stream = (descriptor > 0) ? fdopen(descriptor, "r+b") : NULL; 1483 1484 if (*stream == NULL) { 1485 if (descriptor > 0) 1486 close(descriptor); 1487 free(tempfile_name); 1488 return NULL; 1489 } 1490 1491 return tempfile_name; 1492 } 1493 1494 #ifdef ENABLE_OPERATINGDIR 1495 /* Change to the specified operating directory, when it's valid. */ 1496 void init_operating_dir(void) 1497 { 1498 char *target = get_full_path(operating_dir); 1499 1500 /* If the operating directory is inaccessible, fail. */ 1501 if (target == NULL || chdir(target) == -1) 1502 die(_("Invalid operating directory: %s\n"), operating_dir); 1503 1504 free(operating_dir); 1505 operating_dir = nrealloc(target, strlen(target) + 1); 1506 } 1507 1508 /* Check whether the given path is outside of the operating directory. 1509 * Return TRUE if it is, and FALSE otherwise. If tabbing is TRUE, 1510 * incomplete names that can grow into matches for the operating directory 1511 * are considered to be inside, so that tab completion will work. */ 1512 bool outside_of_confinement(const char *somepath, bool tabbing) 1513 { 1514 bool is_inside, begins_to_be; 1515 char *fullpath; 1516 1517 /* If no operating directory is set, there is nothing to check. */ 1518 if (operating_dir == NULL) 1519 return FALSE; 1520 1521 fullpath = get_full_path(somepath); 1522 1523 /* When we can't get an absolute path, it means some directory in the path 1524 * doesn't exist or is unreadable. When not doing tab completion, somepath 1525 * is what the user typed somewhere. We don't want to report a non-existent 1526 * directory as being outside the operating directory, so we return FALSE. 1527 * When the user is doing tab completion, then somepath exists but is not 1528 * executable. So we say it is outside the operating directory. */ 1529 if (fullpath == NULL) 1530 return tabbing; 1531 1532 is_inside = (strstr(fullpath, operating_dir) == fullpath); 1533 begins_to_be = (tabbing && strstr(operating_dir, fullpath) == operating_dir); 1534 1535 free(fullpath); 1536 1537 return (!is_inside && !begins_to_be); 1538 } 1539 #endif 1540 1541 #ifndef NANO_TINY 1542 /* Transform the specified backup directory to an absolute path, 1543 * and verify that it is usable. */ 1544 void init_backup_dir(void) 1545 { 1546 char *target = get_full_path(backup_dir); 1547 1548 /* If we can't get an absolute path (which means it doesn't exist or 1549 * isn't accessible), or it's not a directory, fail. */ 1550 if (target == NULL || target[strlen(target) - 1] != '/') 1551 die(_("Invalid backup directory: %s\n"), backup_dir); 1552 1553 free(backup_dir); 1554 backup_dir = nrealloc(target, strlen(target) + 1); 1555 } 1556 #endif 1557 1558 /* Read all data from `inn`, and write it to `out`. File `inn` must be open 1559 * for reading, and `out` for writing. Return 0 on success, a negative number 1560 * on read error, and a positive number on write error. File `inn` is always 1561 * closed by this function, `out` is closed only if `close_out` is true. */ 1562 int copy_file(FILE *inn, FILE *out, bool close_out) 1563 { 1564 int retval = 0; 1565 char buf[BUFSIZ]; 1566 size_t charsread; 1567 int (*flush_out_fnc)(FILE *) = (close_out) ? fclose : fflush; 1568 1569 do { 1570 charsread = fread(buf, 1, BUFSIZ, inn); 1571 if (charsread == 0 && ferror(inn)) { 1572 retval = -1; 1573 break; 1574 } 1575 if (fwrite(buf, 1, charsread, out) < charsread) { 1576 retval = 2; 1577 break; 1578 } 1579 } while (charsread > 0); 1580 1581 if (fclose(inn) == EOF) 1582 retval = -3; 1583 if (flush_out_fnc(out) == EOF) 1584 retval = 4; 1585 1586 return retval; 1587 } 1588 1589 #ifndef NANO_TINY 1590 /* Create a backup of an existing file. If the user did not request backups, 1591 * make a temporary one (trying first in the directory of the original file, 1592 * then in the user's home directory). Return TRUE if the save can proceed. */ 1593 bool make_backup_of(char *realname) 1594 { 1595 FILE *original = NULL, *backup_file = NULL; 1596 static struct timespec filetime[2]; 1597 int creation_flags, descriptor; 1598 bool second_attempt = FALSE; 1599 char *backupname = NULL; 1600 int verdict = 0; 1601 1602 /* Remember the original file's access and modification times. */ 1603 filetime[0].tv_sec = openfile->statinfo->st_atime; 1604 filetime[1].tv_sec = openfile->statinfo->st_mtime; 1605 1606 statusbar(_("Making backup...")); 1607 1608 /* If no backup directory was specified, we make a simple backup 1609 * by appending a tilde to the original file name. Otherwise, 1610 * we create a numbered backup in the specified directory. */ 1611 if (backup_dir == NULL) { 1612 backupname = nmalloc(strlen(realname) + 2); 1613 sprintf(backupname, "%s~", realname); 1614 } else { 1615 char *thename = get_full_path(realname); 1616 1617 /* If we have a valid absolute path, replace each slash 1618 * in this full path with an exclamation mark. Otherwise, 1619 * just use the file-name portion of the given path. */ 1620 if (thename) { 1621 for (int i = 0; thename[i] != '\0'; i++) 1622 if (thename[i] == '/') 1623 thename[i] = '!'; 1624 } else 1625 thename = copy_of(tail(realname)); 1626 1627 backupname = nmalloc(strlen(backup_dir) + strlen(thename) + 1); 1628 sprintf(backupname, "%s%s", backup_dir, thename); 1629 free(thename); 1630 1631 thename = get_next_filename(backupname, "~"); 1632 free(backupname); 1633 backupname = thename; 1634 1635 /* If all numbered backup names are taken, the user must 1636 * be fond of backups. Thus, without one, do not go on. */ 1637 if (*backupname == '\0') { 1638 statusline(ALERT, _("Too many existing backup files")); 1639 free(backupname); 1640 return FALSE; 1641 } 1642 } 1643 1644 /* Now first try to delete an existing backup file. */ 1645 if (unlink(backupname) < 0 && errno != ENOENT && !ISSET(INSECURE_BACKUP)) 1646 goto problem; 1647 1648 creation_flags = O_WRONLY|O_CREAT|(ISSET(INSECURE_BACKUP) ? O_TRUNC : O_EXCL); 1649 1650 /* Create the backup file (or truncate the existing one). */ 1651 descriptor = open(backupname, creation_flags, S_IRUSR|S_IWUSR); 1652 1653 retry: 1654 if (descriptor >= 0) 1655 backup_file = fdopen(descriptor, "wb"); 1656 1657 if (backup_file == NULL) 1658 goto problem; 1659 1660 #ifdef HAVE_FCHOWN 1661 /* Try to change owner and group to those of the original file; 1662 * ignore permission errors, as a normal user cannot change the owner. */ 1663 if (fchown(descriptor, openfile->statinfo->st_uid, 1664 openfile->statinfo->st_gid) < 0 && errno != EPERM) { 1665 fclose(backup_file); 1666 goto problem; 1667 } 1668 #endif 1669 #ifdef HAVE_FCHMOD 1670 /* Set the backup's permissions to those of the original file. 1671 * It is not a security issue if this fails, as we have created 1672 * the file with just read and write permission for the owner. */ 1673 if (fchmod(descriptor, openfile->statinfo->st_mode) < 0 && errno != EPERM) { 1674 fclose(backup_file); 1675 goto problem; 1676 } 1677 #endif 1678 1679 original = fopen(realname, "rb"); 1680 1681 /* If opening succeeded, copy the existing file to the backup. */ 1682 if (original != NULL) 1683 verdict = copy_file(original, backup_file, FALSE); 1684 1685 if (original == NULL || verdict < 0) { 1686 warn_and_briefly_pause(_("Cannot read original file")); 1687 fclose(backup_file); 1688 goto failure; 1689 } else if (verdict > 0) { 1690 fclose(backup_file); 1691 goto problem; 1692 } 1693 1694 /* Since this backup is a newly created file, explicitly sync it to 1695 * permanent storage before starting to write out the actual file. */ 1696 if (fflush(backup_file) != 0 || fsync(fileno(backup_file)) != 0) { 1697 fclose(backup_file); 1698 goto problem; 1699 } 1700 1701 /* Set the backup's timestamps to those of the original file. 1702 * Failure is unimportant: saving the file apparently worked. */ 1703 IGNORE_CALL_RESULT(futimens(descriptor, filetime)); 1704 1705 if (fclose(backup_file) == 0) { 1706 free(backupname); 1707 return TRUE; 1708 } 1709 1710 problem: 1711 get_homedir(); 1712 1713 /* If the first attempt of copying the file failed, try again to HOME. */ 1714 if (!second_attempt && homedir) { 1715 unlink(backupname); 1716 free(backupname); 1717 1718 warn_and_briefly_pause(_("Cannot make regular backup")); 1719 warn_and_briefly_pause(_("Trying again in your home directory")); 1720 currmenu = MMOST; 1721 1722 backupname = nmalloc(strlen(homedir) + strlen(tail(realname)) + 9); 1723 sprintf(backupname, "%s/%s~XXXXXX", homedir, tail(realname)); 1724 1725 descriptor = mkstemp(backupname); 1726 backup_file = NULL; 1727 1728 second_attempt = TRUE; 1729 goto retry; 1730 } else 1731 warn_and_briefly_pause(_("Cannot make backup")); 1732 1733 failure: 1734 warn_and_briefly_pause(strerror(errno)); 1735 currmenu = MMOST; 1736 free(backupname); 1737 1738 /* If both attempts failed, and it isn't because of lack of disk space, 1739 * ask the user what to do, because if something goes wrong during the 1740 * save of the file itself, its contents may be lost. */ 1741 /* TRANSLATORS: Try to keep this message at most 76 characters. */ 1742 if (errno != ENOSPC && ask_user(YESORNO, _("Cannot make backup; " 1743 "continue and save actual file? ")) == YES) 1744 return TRUE; 1745 1746 /* TRANSLATORS: The %s is the reason of failure. */ 1747 statusline(HUSH, _("Cannot make backup: %s"), strerror(errno)); 1748 return FALSE; 1749 } 1750 #endif /* !NANO_TINY */ 1751 1752 /* Write the current buffer to disk. If thefile isn't NULL, we write to a 1753 * temporary file that is already open. If normal is FALSE (for a spellcheck 1754 * or an emergency save, for example), we don't make a backup and don't give 1755 * feedback. If method is APPEND or PREPEND, it means we will be appending 1756 * or prepending instead of overwriting the given file. If annotate is TRUE 1757 * and when writing a normal file, we set the current filename and stat info. 1758 * Return TRUE on success, and FALSE otherwise. */ 1759 bool write_file(const char *name, FILE *thefile, bool normal, 1760 kind_of_writing_type method, bool annotate) 1761 { 1762 #ifndef NANO_TINY 1763 bool is_existing_file; 1764 /* Becomes TRUE when the file is non-temporary and exists. */ 1765 struct stat fileinfo; 1766 /* The status fields filled in by statting the file. */ 1767 #endif 1768 char *realname = real_dir_from_tilde(name); 1769 /* The filename after tilde expansion. */ 1770 int descriptor = 0; 1771 /* The descriptor that gets assigned when opening the file. */ 1772 char *tempname = NULL; 1773 /* The name of the temporary file we use when prepending. */ 1774 linestruct *line = openfile->filetop; 1775 /* An iterator for moving through the lines of the buffer. */ 1776 size_t lineswritten = 0; 1777 /* The number of lines written, for feedback on the status bar. */ 1778 1779 #ifdef ENABLE_OPERATINGDIR 1780 /* If we're writing a temporary file, we're probably going outside 1781 * the operating directory, so skip the operating directory test. */ 1782 if (normal && outside_of_confinement(realname, FALSE)) { 1783 statusline(ALERT, _("Can't write outside of %s"), operating_dir); 1784 goto cleanup_and_exit; 1785 } 1786 #endif 1787 #ifndef NANO_TINY 1788 /* Check whether the file (at the end of the symlink) exists. */ 1789 is_existing_file = normal && (stat(realname, &fileinfo) != -1); 1790 1791 /* If we haven't statted this file before (say, the user just specified 1792 * it interactively), stat and save the value now, or else we will chase 1793 * null pointers when we do modtime checks and such during backup. */ 1794 if (openfile->statinfo == NULL && is_existing_file) 1795 stat_with_alloc(realname, &openfile->statinfo); 1796 1797 /* When the user requested a backup, we do this only if the file exists and 1798 * isn't temporary AND the file has not been modified by someone else since 1799 * we opened it (or we are appending/prepending or writing a selection). */ 1800 if (ISSET(MAKE_BACKUP) && is_existing_file && !S_ISFIFO(fileinfo.st_mode) && 1801 openfile->statinfo && 1802 (openfile->statinfo->st_mtime == fileinfo.st_mtime || 1803 method != OVERWRITE || openfile->mark)) { 1804 if (!make_backup_of(realname)) 1805 goto cleanup_and_exit; 1806 } 1807 1808 /* When prepending, first copy the existing file to a temporary file. */ 1809 if (method == PREPEND) { 1810 FILE *source = NULL; 1811 FILE *target = NULL; 1812 int verdict; 1813 1814 if (is_existing_file && S_ISFIFO(fileinfo.st_mode)) { 1815 statusline(ALERT, _("Error writing %s: %s"), realname, "FIFO"); 1816 goto cleanup_and_exit; 1817 } 1818 1819 source = fopen(realname, "rb"); 1820 1821 if (source == NULL) { 1822 statusline(ALERT, _("Error reading %s: %s"), realname, strerror(errno)); 1823 goto cleanup_and_exit; 1824 } 1825 1826 tempname = safe_tempfile(&target); 1827 1828 if (tempname == NULL) { 1829 statusline(ALERT, _("Error writing temp file: %s"), strerror(errno)); 1830 fclose(source); 1831 goto cleanup_and_exit; 1832 } 1833 1834 verdict = copy_file(source, target, TRUE); 1835 1836 if (verdict < 0) { 1837 statusline(ALERT, _("Error reading %s: %s"), realname, strerror(errno)); 1838 unlink(tempname); 1839 goto cleanup_and_exit; 1840 } else if (verdict > 0) { 1841 statusline(ALERT, _("Error writing temp file: %s"), strerror(errno)); 1842 unlink(tempname); 1843 goto cleanup_and_exit; 1844 } 1845 } 1846 1847 if (is_existing_file && S_ISFIFO(fileinfo.st_mode)) 1848 statusbar(_("Writing to FIFO...")); 1849 #endif /* !NANO_TINY */ 1850 1851 /* When it's not a temporary file, this is where we open or create it. 1852 * For an emergency file, access is restricted to just the owner. */ 1853 if (thefile == NULL) { 1854 mode_t permissions = (normal ? RW_FOR_ALL : S_IRUSR|S_IWUSR); 1855 1856 #ifndef NANO_TINY 1857 block_sigwinch(TRUE); 1858 if (normal) 1859 install_handler_for_Ctrl_C(); 1860 #endif 1861 1862 /* Now open the file. Use O_EXCL for an emergency file. */ 1863 descriptor = open(realname, O_WRONLY | O_CREAT | ((method == APPEND) ? 1864 O_APPEND : (normal ? O_TRUNC : O_EXCL)), permissions); 1865 1866 #ifndef NANO_TINY 1867 if (normal) 1868 restore_handler_for_Ctrl_C(); 1869 block_sigwinch(FALSE); 1870 #endif 1871 1872 /* If we couldn't open the file, give up. */ 1873 if (descriptor < 0) { 1874 if (errno == EINTR || errno == 0) 1875 statusline(ALERT, _("Interrupted")); 1876 else 1877 statusline(ALERT, _("Error writing %s: %s"), realname, strerror(errno)); 1878 #ifndef NANO_TINY 1879 if (tempname != NULL) 1880 unlink(tempname); 1881 #endif 1882 goto cleanup_and_exit; 1883 } 1884 1885 thefile = fdopen(descriptor, (method == APPEND) ? "ab" : "wb"); 1886 1887 if (thefile == NULL) { 1888 statusline(ALERT, _("Error writing %s: %s"), realname, strerror(errno)); 1889 close(descriptor); 1890 goto cleanup_and_exit; 1891 } 1892 } 1893 1894 if (normal) 1895 statusbar(_("Writing...")); 1896 1897 while (TRUE) { 1898 size_t data_len, wrote; 1899 1900 /* Decode LFs as the NULs that they are, before writing to disk. */ 1901 data_len = recode_LF_to_NUL(line->data); 1902 1903 wrote = fwrite(line->data, 1, data_len, thefile); 1904 1905 /* Re-encode any embedded NULs as LFs. */ 1906 recode_NUL_to_LF(line->data, data_len); 1907 1908 if (wrote < data_len) { 1909 statusline(ALERT, _("Error writing %s: %s"), realname, strerror(errno)); 1910 fclose(thefile); 1911 goto cleanup_and_exit; 1912 } 1913 1914 /* If we've reached the last line of the buffer, don't write a newline 1915 * character after it. If this last line is empty, it means zero bytes 1916 * are written for it, and we don't count it in the number of lines. */ 1917 if (line->next == NULL) { 1918 if (line->data[0] != '\0') 1919 lineswritten++; 1920 break; 1921 } 1922 1923 #ifndef NANO_TINY 1924 if (openfile->fmt == DOS_FILE || openfile->fmt == MAC_FILE) { 1925 if (putc('\r', thefile) == EOF) { 1926 statusline(ALERT, _("Error writing %s: %s"), realname, strerror(errno)); 1927 fclose(thefile); 1928 goto cleanup_and_exit; 1929 } 1930 } 1931 1932 if (openfile->fmt != MAC_FILE) 1933 #endif 1934 if (putc('\n', thefile) == EOF) { 1935 statusline(ALERT, _("Error writing %s: %s"), realname, strerror(errno)); 1936 fclose(thefile); 1937 goto cleanup_and_exit; 1938 } 1939 1940 line = line->next; 1941 lineswritten++; 1942 } 1943 1944 #ifndef NANO_TINY 1945 /* When prepending, append the temporary file to what we wrote above. */ 1946 if (method == PREPEND) { 1947 FILE *source = fopen(tempname, "rb"); 1948 int verdict; 1949 1950 if (source == NULL) { 1951 statusline(ALERT, _("Error reading temp file: %s"), strerror(errno)); 1952 fclose(thefile); 1953 goto cleanup_and_exit; 1954 } 1955 1956 verdict = copy_file(source, thefile, FALSE); 1957 1958 if (verdict < 0) { 1959 statusline(ALERT, _("Error reading temp file: %s"), strerror(errno)); 1960 fclose(thefile); 1961 goto cleanup_and_exit; 1962 } else if (verdict > 0) { 1963 statusline(ALERT, _("Error writing %s: %s"), realname, strerror(errno)); 1964 fclose(thefile); 1965 goto cleanup_and_exit; 1966 } 1967 1968 unlink(tempname); 1969 } 1970 1971 if (!is_existing_file || !S_ISFIFO(fileinfo.st_mode)) 1972 /* Ensure the data has reached the disk before reporting it as written. */ 1973 if (fflush(thefile) != 0 || fsync(fileno(thefile)) != 0) { 1974 statusline(ALERT, _("Error writing %s: %s"), realname, strerror(errno)); 1975 fclose(thefile); 1976 goto cleanup_and_exit; 1977 } 1978 #endif 1979 1980 #if !defined(NANO_TINY) && defined(HAVE_CHMOD) && defined(HAVE_CHOWN) 1981 /* Change permissions and owner of an emergency save file to the values 1982 * of the original file, but ignore any failure as we are in a hurry. */ 1983 if (method == EMERGENCY && descriptor && openfile->statinfo) { 1984 IGNORE_CALL_RESULT(fchmod(descriptor, openfile->statinfo->st_mode)); 1985 IGNORE_CALL_RESULT(fchown(descriptor, openfile->statinfo->st_uid, 1986 openfile->statinfo->st_gid)); 1987 } 1988 #endif 1989 1990 if (fclose(thefile) != 0) { 1991 statusline(ALERT, _("Error writing %s: %s"), realname, strerror(errno)); 1992 1993 cleanup_and_exit: 1994 #ifndef NANO_TINY 1995 if (errno == ENOSPC && normal) { 1996 napms(3200); lastmessage = VACUUM; 1997 /* TRANSLATORS: This warns for data loss when the disk is full. */ 1998 statusline(ALERT, _("File on disk has been truncated!")); 1999 napms(3200); lastmessage = VACUUM; 2000 /* TRANSLATORS: This is a suggestion to the user, 2001 * where "resume" means resuming from suspension. 2002 * Try to keep this at most 76 characters. */ 2003 statusline(ALERT, _("Maybe ^T^Z, make room on disk, resume, then ^S^X")); 2004 stat_with_alloc(realname, &openfile->statinfo); 2005 } 2006 #endif 2007 free(tempname); 2008 free(realname); 2009 return FALSE; 2010 } 2011 2012 /* When having written an entire buffer, update some administrivia. */ 2013 if (annotate && method == OVERWRITE) { 2014 /* If the filename was changed, write a new lockfile when needed, 2015 * and check whether it means a different syntax gets used. */ 2016 if (strcmp(openfile->filename, realname) != 0) { 2017 #ifndef NANO_TINY 2018 if (openfile->lock_filename != NULL) { 2019 delete_lockfile(openfile->lock_filename); 2020 free(openfile->lock_filename); 2021 } 2022 2023 if (ISSET(LOCKING)) 2024 openfile->lock_filename = do_lockfile(realname, FALSE); 2025 #endif 2026 openfile->filename = mallocstrcpy(openfile->filename, realname); 2027 #ifdef ENABLE_COLOR 2028 const char *oldname, *newname; 2029 2030 oldname = openfile->syntax ? openfile->syntax->name : ""; 2031 find_and_prime_applicable_syntax(); 2032 newname = openfile->syntax ? openfile->syntax->name : ""; 2033 2034 /* If the syntax changed, discard and recompute the multidata. */ 2035 if (strcmp(oldname, newname) != 0) { 2036 for (line = openfile->filetop; line != NULL; line = line->next) { 2037 free(line->multidata); 2038 line->multidata = NULL; 2039 } 2040 2041 precalc_multicolorinfo(); 2042 have_palette = FALSE; 2043 refresh_needed = TRUE; 2044 } 2045 #endif 2046 } 2047 #ifndef NANO_TINY 2048 /* Get or update the stat info to reflect the current state. */ 2049 stat_with_alloc(realname, &openfile->statinfo); 2050 2051 /* Record at which point in the undo stack the buffer was saved. */ 2052 openfile->last_saved = openfile->current_undo; 2053 openfile->last_action = OTHER; 2054 #endif 2055 openfile->modified = FALSE; 2056 titlebar(NULL); 2057 } 2058 2059 #ifndef NANO_TINY 2060 if (ISSET(MINIBAR) && !ISSET(ZERO) && LINES > 1 && annotate) 2061 report_size = TRUE; 2062 else 2063 #endif 2064 if (normal) 2065 statusline(REMARK, P_("Wrote %zu line", "Wrote %zu lines", 2066 lineswritten), lineswritten); 2067 2068 free(tempname); 2069 free(realname); 2070 2071 return TRUE; 2072 } 2073 2074 #ifndef NANO_TINY 2075 /* Write the marked region of the current buffer out to disk. 2076 * Return TRUE on success and FALSE on error. */ 2077 bool write_region_to_file(const char *name, FILE *stream, bool normal, 2078 kind_of_writing_type method) 2079 { 2080 linestruct *birthline, *topline, *botline, *stopper, *afterline; 2081 char *was_datastart, saved_byte; 2082 size_t top_x, bot_x; 2083 bool retval; 2084 2085 get_region(&topline, &top_x, &botline, &bot_x); 2086 2087 /* When needed, prepare a magic end line for the region. */ 2088 if (normal && bot_x > 0 && !ISSET(NO_NEWLINES)) { 2089 stopper = make_new_node(botline); 2090 stopper->data = copy_of(""); 2091 } else 2092 stopper = NULL; 2093 2094 /* Make the marked area look like a separate buffer. */ 2095 afterline = botline->next; 2096 botline->next = stopper; 2097 saved_byte = botline->data[bot_x]; 2098 botline->data[bot_x] = '\0'; 2099 was_datastart = topline->data; 2100 topline->data += top_x; 2101 birthline = openfile->filetop; 2102 openfile->filetop = topline; 2103 2104 retval = write_file(name, stream, normal, method, NONOTES); 2105 2106 /* Restore the proper state of the buffer. */ 2107 openfile->filetop = birthline; 2108 topline->data = was_datastart; 2109 botline->data[bot_x] = saved_byte; 2110 botline->next = afterline; 2111 2112 if (stopper) 2113 delete_node(stopper); 2114 2115 return retval; 2116 } 2117 #endif /* !NANO_TINY */ 2118 2119 /* Write the current buffer (or marked region) to disk. If exiting is TRUE, 2120 * write the entire buffer regardless of whether the mark is on. Do not ask 2121 * for a name when withprompt is FALSE (nor when doing save-on-exit and the 2122 * buffer already has a name). Return 0 on error, 1 on success, and 2 when 2123 * the buffer is to be discarded. */ 2124 int write_it_out(bool exiting, bool withprompt) 2125 { 2126 char *given; 2127 /* The filename we offer, or what the user typed so far. */ 2128 bool maychange = (openfile->filename[0] == '\0'); 2129 /* Whether it's okay to save the buffer under a different name. */ 2130 kind_of_writing_type method = OVERWRITE; 2131 #ifdef ENABLE_EXTRA 2132 static bool did_credits = FALSE; 2133 #endif 2134 2135 /* Display newlines in filenames as ^J. */ 2136 as_an_at = FALSE; 2137 2138 #ifndef NANO_TINY 2139 given = copy_of((openfile->mark && !exiting) ? "" : openfile->filename); 2140 #else 2141 given = copy_of(openfile->filename); 2142 #endif 2143 2144 while (TRUE) { 2145 functionptrtype function; 2146 const char *msg; 2147 int response = 0; 2148 int choice = NO; 2149 #ifndef NANO_TINY 2150 const char *formatstr = (openfile->fmt == DOS_FILE) ? _(" [DOS Format]") : 2151 (openfile->fmt == MAC_FILE) ? _(" [Mac Format]") : ""; 2152 const char *backupstr = ISSET(MAKE_BACKUP) ? _(" [Backup]") : ""; 2153 2154 /* When the mark is on, offer to write the selection to disk, but 2155 * not when in restricted mode, because it would allow writing to 2156 * a file not specified on the command line. */ 2157 if (openfile->mark && !exiting && !ISSET(RESTRICTED)) 2158 /* TRANSLATORS: The next six strings are prompts. */ 2159 msg = (method == PREPEND) ? _("Prepend Selection to File") : 2160 (method == APPEND) ? _("Append Selection to File") : 2161 _("Write Selection to File"); 2162 else if (method != OVERWRITE) 2163 /* TRANSLATORS: Next three prompts are analogous to the above three. */ 2164 msg = (method == PREPEND) ? _("Prepend to File") : _("Append to File"); 2165 else 2166 #endif 2167 msg = _("Write to File"); 2168 2169 present_path = mallocstrcpy(present_path, "./"); 2170 2171 /* When we shouldn't prompt, use the existing filename. 2172 * Otherwise, ask for (confirmation of) the filename. */ 2173 if ((!withprompt || (ISSET(SAVE_ON_EXIT) && exiting)) && 2174 openfile->filename[0] != '\0') 2175 answer = mallocstrcpy(answer, openfile->filename); 2176 else 2177 response = do_prompt(MWRITEFILE, given, NULL, 2178 edit_refresh, "%s%s%s", msg, 2179 #ifndef NANO_TINY 2180 formatstr, backupstr 2181 #else 2182 "", "" 2183 #endif 2184 ); 2185 2186 if (response < 0) { 2187 statusbar(_("Cancelled")); 2188 free(given); 2189 return 0; 2190 } 2191 2192 function = func_from_key(response); 2193 2194 /* Upon request, abandon the buffer. */ 2195 if (function == discard_buffer) { 2196 final_status = 2; /* ^O^Q makes nano exit with an error. */ 2197 free(given); 2198 return 2; 2199 } 2200 2201 given = mallocstrcpy(given, answer); 2202 2203 #ifdef ENABLE_BROWSER 2204 if (function == to_files && !ISSET(RESTRICTED)) { 2205 char *chosen = browse_in(answer); 2206 2207 if (chosen == NULL) 2208 continue; 2209 2210 free(answer); 2211 answer = chosen; 2212 } else 2213 #endif 2214 #ifndef NANO_TINY 2215 if (function == dos_format) { 2216 openfile->fmt = (openfile->fmt == DOS_FILE) ? NIX_FILE : DOS_FILE; 2217 continue; 2218 } else if (function == mac_format) { 2219 openfile->fmt = (openfile->fmt == MAC_FILE) ? NIX_FILE : MAC_FILE; 2220 continue; 2221 } else if (function == back_it_up && !ISSET(RESTRICTED)) { 2222 TOGGLE(MAKE_BACKUP); 2223 continue; 2224 } else if ((function == prepend_it || function == append_it) && !ISSET(RESTRICTED)) { 2225 if (function == prepend_it) 2226 method = (method == PREPEND) ? OVERWRITE : PREPEND; 2227 else 2228 method = (method == APPEND) ? OVERWRITE : APPEND; 2229 if (strcmp(answer, openfile->filename) == 0) 2230 given[0] = '\0'; 2231 continue; 2232 } else 2233 #endif 2234 if (function == do_help) 2235 continue; 2236 2237 #ifdef ENABLE_EXTRA 2238 /* If the user pressed Ctrl-X in the edit window, and answered "Y" at 2239 * the "Save modified buffer?" prompt, and entered "zzy" as filename, 2240 * and this is the first time around, show an Easter egg. */ 2241 if (exiting && !ISSET(SAVE_ON_EXIT) && openfile->filename[0] == '\0' && 2242 strcmp(answer, "zzy") == 0 && !did_credits) { 2243 if (LINES > 5 && COLS > 31) { 2244 do_credits(); 2245 did_credits = TRUE; 2246 } else 2247 /* TRANSLATORS: Concisely say the screen is too small. */ 2248 statusline(AHEM, _("Too tiny")); 2249 2250 free(given); 2251 return 0; 2252 } 2253 #endif 2254 2255 if (method == OVERWRITE) { 2256 bool name_exists, do_warning; 2257 char *full_answer, *full_filename; 2258 struct stat fileinfo; 2259 2260 full_answer = get_full_path(answer); 2261 full_filename = get_full_path(openfile->filename); 2262 name_exists = (stat((full_answer == NULL) ? 2263 answer : full_answer, &fileinfo) != -1); 2264 2265 if (openfile->filename[0] == '\0') 2266 do_warning = name_exists; 2267 else 2268 do_warning = (strcmp((full_answer == NULL) ? 2269 answer : full_answer, (full_filename == NULL) ? 2270 openfile->filename : full_filename) != 0); 2271 2272 free(full_filename); 2273 free(full_answer); 2274 2275 if (do_warning) { 2276 /* When in restricted mode, we aren't allowed to overwrite 2277 * an existing file with the current buffer, nor to change 2278 * the name of the current buffer if it already has one. */ 2279 if (ISSET(RESTRICTED)) { 2280 /* TRANSLATORS: Restricted mode forbids overwriting. */ 2281 warn_and_briefly_pause(_("File exists -- cannot overwrite")); 2282 continue; 2283 } 2284 2285 if (!maychange) { 2286 #ifndef NANO_TINY 2287 if (exiting || !openfile->mark) 2288 #endif 2289 { 2290 if (ask_user(YESORNO, _("Save file under " 2291 "DIFFERENT NAME? ")) != YES) 2292 continue; 2293 maychange = TRUE; 2294 } 2295 } 2296 2297 if (name_exists) { 2298 char *question = _("File \"%s\" exists; OVERWRITE? "); 2299 char *name = crop_to_fit(answer, COLS - breadth(question) + 1); 2300 char *message = nmalloc(strlen(question) + strlen(name) + 1); 2301 2302 sprintf(message, question, name); 2303 2304 choice = ask_user(YESORNO, message); 2305 2306 free(message); 2307 free(name); 2308 2309 if (choice != YES) 2310 continue; 2311 } 2312 } 2313 #ifndef NANO_TINY 2314 /* Complain if the file exists, the name hasn't changed, 2315 * and the stat information we had before does not match 2316 * what we have now. */ 2317 else if (name_exists && openfile->statinfo && 2318 (openfile->statinfo->st_mtime < fileinfo.st_mtime || 2319 openfile->statinfo->st_dev != fileinfo.st_dev || 2320 openfile->statinfo->st_ino != fileinfo.st_ino)) { 2321 2322 warn_and_briefly_pause(_("File on disk has changed")); 2323 2324 /* TRANSLATORS: Try to keep this at most 76 characters. */ 2325 choice = ask_user(YESORNO, _("File was modified " 2326 "since you opened it; continue saving? ")); 2327 wipe_statusbar(); 2328 2329 /* When in tool mode and not called by 'savefile', 2330 * overwrite the file right here when requested. */ 2331 if (ISSET(SAVE_ON_EXIT) && withprompt) { 2332 free(given); 2333 if (choice == YES) 2334 return write_file(openfile->filename, NULL, 2335 NORMAL, OVERWRITE, NONOTES); 2336 else if (choice == NO) /* Discard buffer */ 2337 return 2; 2338 else 2339 return 0; 2340 } else if (choice == CANCEL && exiting) { 2341 continue; 2342 } else if (choice != YES) { 2343 free(given); 2344 return 1; 2345 } 2346 } 2347 #endif 2348 } 2349 2350 free(given); 2351 break; 2352 } 2353 2354 /* When the mark is on (and we've prompted for a name and we're 2355 * not exiting and we're not in restricted mode), then write out 2356 * the marked region; otherwise, write out the whole buffer. */ 2357 #ifndef NANO_TINY 2358 if (openfile->mark && withprompt && !exiting && !ISSET(RESTRICTED)) 2359 return write_region_to_file(answer, NULL, NORMAL, method); 2360 else 2361 #endif 2362 return write_file(answer, NULL, NORMAL, method, ANNOTATE); 2363 } 2364 2365 /* Write the current buffer to disk, or discard it. */ 2366 void do_writeout(void) 2367 { 2368 /* If the user chose to discard the buffer, close it. */ 2369 if (write_it_out(FALSE, TRUE) == 2) 2370 close_and_go(); 2371 } 2372 2373 /* If it has a name, write the current buffer to disk without prompting. */ 2374 void do_savefile(void) 2375 { 2376 if (write_it_out(FALSE, FALSE) == 2) 2377 close_and_go(); 2378 } 2379 2380 /* Convert the tilde notation when the given path begins with ~/ or ~user/. 2381 * Return an allocated string containing the expanded path. */ 2382 char *real_dir_from_tilde(const char *path) 2383 { 2384 char *tilded, *retval; 2385 size_t i = 1; 2386 2387 if (*path != '~') 2388 return copy_of(path); 2389 2390 /* Figure out how much of the string we need to compare. */ 2391 while (path[i] != '/' && path[i] != '\0') 2392 i++; 2393 2394 if (i == 1) { 2395 get_homedir(); 2396 tilded = copy_of(homedir); 2397 } else { 2398 #ifdef HAVE_PWD_H 2399 const struct passwd *userdata; 2400 2401 tilded = measured_copy(path, i); 2402 2403 do { 2404 userdata = getpwent(); 2405 } while (userdata && strcmp(userdata->pw_name, tilded + 1) != 0); 2406 endpwent(); 2407 2408 if (userdata != NULL) 2409 tilded = mallocstrcpy(tilded, userdata->pw_dir); 2410 #else 2411 tilded = copy_of(""); 2412 #endif 2413 } 2414 2415 retval = nmalloc(strlen(tilded) + strlen(path + i) + 1); 2416 sprintf(retval, "%s%s", tilded, path + i); 2417 2418 free(tilded); 2419 2420 return retval; 2421 } 2422 2423 #if defined(ENABLE_TABCOMP) || defined(ENABLE_BROWSER) 2424 /* Our sort routine for file listings. Sort alphabetically and 2425 * case-insensitively, and sort directories before filenames. */ 2426 int diralphasort(const void *va, const void *vb) 2427 { 2428 struct stat fileinfo; 2429 const char *a = *(const char *const *)va; 2430 const char *b = *(const char *const *)vb; 2431 bool aisdir = stat(a, &fileinfo) != -1 && S_ISDIR(fileinfo.st_mode); 2432 bool bisdir = stat(b, &fileinfo) != -1 && S_ISDIR(fileinfo.st_mode); 2433 2434 if (aisdir && !bisdir) 2435 return -1; 2436 if (!aisdir && bisdir) 2437 return 1; 2438 2439 int difference = mbstrcasecmp(a, b); 2440 2441 /* If two names are equivalent when ignoring case, compare them bytewise. */ 2442 if (difference == 0) 2443 return strcmp(a, b); 2444 else 2445 return difference; 2446 } 2447 #endif 2448 2449 #ifdef ENABLE_TABCOMP 2450 /* Return TRUE when the given path is a directory. */ 2451 bool is_dir(const char *path) 2452 { 2453 char *thepath = real_dir_from_tilde(path); 2454 struct stat fileinfo; 2455 bool retval; 2456 2457 retval = (stat(thepath, &fileinfo) != -1 && S_ISDIR(fileinfo.st_mode)); 2458 2459 free(thepath); 2460 2461 return retval; 2462 } 2463 2464 /* Try to complete the given fragment of given length to a username. */ 2465 char **username_completion(const char *morsel, size_t length, size_t *num_matches) 2466 { 2467 char **matches = NULL; 2468 #ifdef HAVE_PWD_H 2469 const struct passwd *userdata; 2470 2471 /* Iterate through the entries in the passwd file, and 2472 * add each fitting username to the list of matches. */ 2473 while ((userdata = getpwent()) != NULL) { 2474 if (strncmp(userdata->pw_name, morsel + 1, length - 1) == 0) { 2475 #ifdef ENABLE_OPERATINGDIR 2476 /* Skip directories that are outside of the allowed area. */ 2477 if (outside_of_confinement(userdata->pw_dir, TRUE)) 2478 continue; 2479 #endif 2480 matches = nrealloc(matches, (*num_matches + 1) * sizeof(char *)); 2481 matches[*num_matches] = nmalloc(strlen(userdata->pw_name) + 2); 2482 sprintf(matches[*num_matches], "~%s", userdata->pw_name); 2483 ++(*num_matches); 2484 } 2485 } 2486 2487 endpwent(); 2488 #endif 2489 2490 return matches; 2491 } 2492 2493 /* The next two functions were adapted from busybox 0.46 (cmdedit.c). 2494 * Here is the tweaked notice from that file: 2495 * 2496 * Termios command-line History and Editing, originally intended for NetBSD. 2497 * Copyright (C) 1999, 2000 2498 * Main code: Adam Rogoyski <rogoyski@cs.utexas.edu> 2499 * Etc: Dave Cinege <dcinege@psychosis.com> 2500 * Adjusted/rewritten: Erik Andersen <andersee@debian.org> 2501 * 2502 * You may use this code as you wish, so long as the original author(s) 2503 * are attributed in any redistributions of the source code. 2504 * This code is 'as is' with no warranty. 2505 * This code may safely be consumed by a BSD or GPL license. */ 2506 2507 /* Try to complete the given fragment to an existing filename. */ 2508 char **filename_completion(const char *morsel, size_t *num_matches) 2509 { 2510 char *dirname = copy_of(morsel); 2511 char *slash, *filename; 2512 size_t filenamelen; 2513 char *fullname = NULL; 2514 char **matches = NULL; 2515 const struct dirent *entry; 2516 DIR *dir; 2517 2518 /* If there's a / in the name, split out filename and directory parts. */ 2519 slash = strrchr(dirname, '/'); 2520 if (slash != NULL) { 2521 char *wasdirname = dirname; 2522 2523 filename = copy_of(++slash); 2524 /* Cut off the filename part after the slash. */ 2525 *slash = '\0'; 2526 dirname = real_dir_from_tilde(dirname); 2527 /* A non-absolute path is relative to the current browser directory. */ 2528 if (dirname[0] != '/') { 2529 dirname = nrealloc(dirname, strlen(present_path) + strlen(wasdirname) + 1); 2530 sprintf(dirname, "%s%s", present_path, wasdirname); 2531 } 2532 free(wasdirname); 2533 } else { 2534 filename = dirname; 2535 dirname = copy_of(present_path); 2536 } 2537 2538 dir = opendir(dirname); 2539 2540 if (dir == NULL) { 2541 beep(); 2542 free(filename); 2543 free(dirname); 2544 return NULL; 2545 } 2546 2547 filenamelen = strlen(filename); 2548 2549 /* Iterate through the filenames in the directory, 2550 * and add each fitting one to the list of matches. */ 2551 while ((entry = readdir(dir)) != NULL) { 2552 if (strncmp(entry->d_name, filename, filenamelen) == 0 && 2553 strcmp(entry->d_name, ".") != 0 && 2554 strcmp(entry->d_name, "..") != 0) { 2555 fullname = nrealloc(fullname, strlen(dirname) + strlen(entry->d_name) + 1); 2556 sprintf(fullname, "%s%s", dirname, entry->d_name); 2557 2558 #ifdef ENABLE_OPERATINGDIR 2559 if (outside_of_confinement(fullname, TRUE)) 2560 continue; 2561 #endif 2562 if (currmenu == MGOTODIR && !is_dir(fullname)) 2563 continue; 2564 2565 matches = nrealloc(matches, (*num_matches + 1) * sizeof(char *)); 2566 matches[*num_matches] = copy_of(entry->d_name); 2567 ++(*num_matches); 2568 } 2569 } 2570 2571 closedir(dir); 2572 free(dirname); 2573 free(filename); 2574 free(fullname); 2575 2576 return matches; 2577 } 2578 2579 /* Do tab completion. 'place' is the position of the status-bar cursor, and 2580 * 'refresh_func' is the function to be called to refresh the edit window. */ 2581 char *input_tab(char *morsel, size_t *place, void (*refresh_func)(void), bool *listed) 2582 { 2583 size_t num_matches = 0; 2584 char **matches = NULL; 2585 2586 /* If the cursor is not at the end of the fragment, do nothing. */ 2587 if (morsel[*place] != '\0') { 2588 beep(); 2589 return morsel; 2590 } 2591 2592 /* If the fragment starts with a tilde and contains no slash, 2593 * then try completing it as a username. */ 2594 if (morsel[0] == '~' && strchr(morsel, '/') == NULL) 2595 matches = username_completion(morsel, *place, &num_matches); 2596 2597 /* If there are no matches yet, try matching against filenames. */ 2598 if (matches == NULL) 2599 matches = filename_completion(morsel, &num_matches); 2600 2601 /* If possible completions were listed before but none will be listed now... */ 2602 if (*listed && num_matches < 2) { 2603 refresh_func(); 2604 *listed = FALSE; 2605 } 2606 2607 if (matches == NULL) { 2608 beep(); 2609 return morsel; 2610 } 2611 2612 const char *lastslash = revstrstr(morsel, "/", morsel + *place); 2613 size_t length_of_path = (lastslash == NULL) ? 0 : lastslash - morsel + 1; 2614 size_t match, common_len = 0; 2615 char *shared, *glued; 2616 char char1[MAXCHARLEN], char2[MAXCHARLEN]; 2617 int len1, len2; 2618 2619 /* Determine the number of characters that all matches have in common. */ 2620 while (TRUE) { 2621 len1 = collect_char(matches[0] + common_len, char1); 2622 2623 for (match = 1; match < num_matches; match++) { 2624 len2 = collect_char(matches[match] + common_len, char2); 2625 2626 if (len1 != len2 || strncmp(char1, char2, len2) != 0) 2627 break; 2628 } 2629 2630 if (match < num_matches || matches[0][common_len] == '\0') 2631 break; 2632 2633 common_len += len1; 2634 } 2635 2636 shared = nmalloc(length_of_path + common_len + 1); 2637 2638 strncpy(shared, morsel, length_of_path); 2639 strncpy(shared + length_of_path, matches[0], common_len); 2640 2641 common_len += length_of_path; 2642 shared[common_len] = '\0'; 2643 2644 /* Cover also the case of the user specifying a relative path. */ 2645 glued = nmalloc(strlen(present_path) + common_len + 1); 2646 sprintf(glued, "%s%s", present_path, shared); 2647 2648 if (num_matches == 1 && (is_dir(shared) || is_dir(glued))) 2649 shared[common_len++] = '/'; 2650 2651 /* If the matches have something in common, copy that part. */ 2652 if (common_len != *place) { 2653 morsel = nrealloc(morsel, common_len + 1); 2654 strncpy(morsel, shared, common_len); 2655 morsel[common_len] = '\0'; 2656 *place = common_len; 2657 } else if (num_matches == 1) 2658 beep(); 2659 2660 /* If there is more than one possible completion, show a sorted list. */ 2661 if (num_matches > 1) { 2662 int lastrow = editwinrows - 1 - (ISSET(ZERO) && LINES > 1 ? 1 : 0); 2663 size_t longest_name = 0; 2664 size_t nrows, ncols; 2665 int row; 2666 2667 if (!*listed) 2668 beep(); 2669 2670 qsort(matches, num_matches, sizeof(char *), diralphasort); 2671 2672 /* Find the length of the longest name among the matches. */ 2673 for (match = 0; match < num_matches; match++) { 2674 size_t namelen = breadth(matches[match]); 2675 2676 if (namelen > longest_name) 2677 longest_name = namelen; 2678 } 2679 2680 if (longest_name > COLS - 1) 2681 longest_name = COLS - 1; 2682 2683 /* The columns of names will be separated by two spaces, 2684 * but the last column will have just one space after it. */ 2685 ncols = (COLS + 1) / (longest_name + 2); 2686 nrows = (num_matches + ncols - 1) / ncols; 2687 2688 row = (nrows < lastrow) ? lastrow - nrows : 0; 2689 2690 /* Blank the edit window and hide the cursor. */ 2691 blank_edit(); 2692 curs_set(0); 2693 2694 /* Now print the list of matches out there. */ 2695 for (match = 0; match < num_matches; match++) { 2696 char *disp; 2697 2698 wmove(midwin, row, (longest_name + 2) * (match % ncols)); 2699 2700 if (row == lastrow && (match + 1) % ncols == 0 && 2701 match + 1 < num_matches) { 2702 waddstr(midwin, _("(more)")); 2703 break; 2704 } 2705 2706 disp = display_string(matches[match], 0, longest_name, FALSE, FALSE); 2707 waddstr(midwin, disp); 2708 free(disp); 2709 2710 if ((match + 1) % ncols == 0) 2711 row++; 2712 } 2713 2714 wnoutrefresh(midwin); 2715 *listed = TRUE; 2716 } 2717 2718 free_chararray(matches, num_matches); 2719 free(glued); 2720 free(shared); 2721 2722 return morsel; 2723 } 2724 #endif /* ENABLE_TABCOMP */