Add Nano tool - user-friendly text editor
[tomato.git] / release / src / router / nano / src / files.c
blobc5b9d6a792562de6f6f6eaabfb6165c0cb350735
1 /* $Id: files.c 4520 2010-11-12 06:23:14Z astyanax $ */
2 /**************************************************************************
3 * files.c *
4 * *
5 * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, *
6 * 2008, 2009 Free Software Foundation, Inc. *
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 3, 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 **************************************************************************/
24 #include "proto.h"
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>
35 /* Add an entry to the openfile openfilestruct. This should only be
36 * called from open_buffer(). */
37 void make_new_buffer(void)
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;
51 /* Initialize the new buffer. */
52 initialize_buffer();
55 /* Initialize the current entry of the openfile openfilestruct. */
56 void initialize_buffer(void)
58 assert(openfile != NULL);
60 openfile->filename = mallocstrcpy(NULL, "");
62 initialize_buffer_text();
64 openfile->current_x = 0;
65 openfile->placewewant = 0;
66 openfile->current_y = 0;
68 openfile->modified = FALSE;
69 #ifndef NANO_TINY
70 openfile->mark_set = FALSE;
72 openfile->mark_begin = NULL;
73 openfile->mark_begin_x = 0;
75 openfile->fmt = NIX_FILE;
77 openfile->current_stat = NULL;
78 openfile->undotop = NULL;
79 openfile->current_undo = NULL;
80 #endif
81 #ifdef ENABLE_COLOR
82 openfile->colorstrings = NULL;
83 #endif
86 /* Initialize the text of the current entry of the openfile
87 * openfilestruct. */
88 void initialize_buffer_text(void)
90 assert(openfile != NULL);
92 openfile->fileage = make_new_node(NULL);
93 openfile->fileage->data = mallocstrcpy(NULL, "");
95 openfile->filebot = openfile->fileage;
96 openfile->edittop = openfile->fileage;
97 openfile->current = openfile->fileage;
99 #ifdef ENABLE_COLOR
100 openfile->fileage->multidata = NULL;
101 #endif
103 openfile->totsize = 0;
106 /* If it's not "", filename is a file to open. We make a new buffer, if
107 * necessary, and then open and read the file, if applicable. */
108 void open_buffer(const char *filename, bool undoable)
110 bool new_buffer = (openfile == NULL
111 #ifdef ENABLE_MULTIBUFFER
112 || ISSET(MULTIBUFFER)
113 #endif
115 /* Whether we load into this buffer or a new one. */
116 FILE *f;
117 int rc;
118 /* rc == -2 means that we have a new file. -1 means that the
119 * open() failed. 0 means that the open() succeeded. */
121 assert(filename != NULL);
123 #ifndef DISABLE_OPERATINGDIR
124 if (check_operating_dir(filename, FALSE)) {
125 statusbar(_("Can't insert file from outside of %s"),
126 operating_dir);
127 return;
129 #endif
131 /* If the filename isn't blank, open the file. Otherwise, treat it
132 * as a new file. */
133 rc = (filename[0] != '\0') ? open_file(filename, new_buffer, &f) :
136 /* If we're loading into a new buffer, add a new entry to
137 * openfile. */
138 if (new_buffer)
139 make_new_buffer();
141 /* If we have a file, and we're loading into a new buffer, update
142 * the filename. */
143 if (rc != -1 && new_buffer)
144 openfile->filename = mallocstrcpy(openfile->filename, filename);
146 /* If we have a non-new file, read it in. Then, if the buffer has
147 * no stat, update the stat, if applicable. */
148 if (rc > 0) {
149 read_file(f, rc, filename, undoable, new_buffer);
150 #ifndef NANO_TINY
151 if (openfile->current_stat == NULL) {
152 openfile->current_stat =
153 (struct stat *)nmalloc(sizeof(struct stat));
154 stat(filename, openfile->current_stat);
156 #endif
159 /* If we have a file, and we're loading into a new buffer, move back
160 * to the beginning of the first line of the buffer. */
161 if (rc != -1 && new_buffer) {
162 openfile->current = openfile->fileage;
163 openfile->current_x = 0;
164 openfile->placewewant = 0;
167 #ifdef ENABLE_COLOR
168 /* If we're loading into a new buffer, update the colors to account
169 * for it, if applicable. */
170 if (new_buffer)
171 color_update();
172 #endif
175 #ifndef DISABLE_SPELLER
176 /* If it's not "", filename is a file to open. We blow away the text of
177 * the current buffer, and then open and read the file, if
178 * applicable. Note that we skip the operating directory test when
179 * doing this. */
180 void replace_buffer(const char *filename)
182 FILE *f;
183 int rc;
184 /* rc == -2 means that we have a new file. -1 means that the
185 * open() failed. 0 means that the open() succeeded. */
187 assert(filename != NULL);
189 /* If the filename isn't blank, open the file. Otherwise, treat it
190 * as a new file. */
191 rc = (filename[0] != '\0') ? open_file(filename, TRUE, &f) : -2;
193 /* Reinitialize the text of the current buffer. */
194 free_filestruct(openfile->fileage);
195 initialize_buffer_text();
197 /* If we have a non-new file, read it in. */
198 if (rc > 0)
199 read_file(f, rc, filename, FALSE, TRUE);
201 /* Move back to the beginning of the first line of the buffer. */
202 openfile->current = openfile->fileage;
203 openfile->current_x = 0;
204 openfile->placewewant = 0;
206 #endif /* !DISABLE_SPELLER */
208 /* Update the screen to account for the current buffer. */
209 void display_buffer(void)
211 /* Update the titlebar, since the filename may have changed. */
212 titlebar(NULL);
214 #ifdef ENABLE_COLOR
215 /* Make sure we're using the buffer's associated colors, if
216 * applicable. */
217 color_init();
218 #endif
220 /* Update the edit window. */
221 edit_refresh();
224 #ifdef ENABLE_MULTIBUFFER
225 /* Switch to the next file buffer if next_buf is TRUE. Otherwise,
226 * switch to the previous file buffer. */
227 void switch_to_prevnext_buffer(bool next_buf)
229 assert(openfile != NULL);
231 /* If only one file buffer is open, indicate it on the statusbar and
232 * get out. */
233 if (openfile == openfile->next) {
234 statusbar(_("No more open file buffers"));
235 return;
238 /* Switch to the next or previous file buffer, depending on the
239 * value of next_buf. */
240 openfile = next_buf ? openfile->next : openfile->prev;
242 #ifdef DEBUG
243 fprintf(stderr, "filename is %s\n", openfile->filename);
244 #endif
246 /* Update the screen to account for the current buffer. */
247 display_buffer();
249 /* Indicate the switch on the statusbar. */
250 statusbar(_("Switched to %s"),
251 ((openfile->filename[0] == '\0') ? _("New Buffer") :
252 openfile->filename));
254 #ifdef DEBUG
255 dump_filestruct(openfile->current);
256 #endif
259 /* Switch to the previous entry in the openfile filebuffer. */
260 void switch_to_prev_buffer_void(void)
262 switch_to_prevnext_buffer(FALSE);
265 /* Switch to the next entry in the openfile filebuffer. */
266 void switch_to_next_buffer_void(void)
268 switch_to_prevnext_buffer(TRUE);
271 /* Delete an entry from the openfile filebuffer, and switch to the one
272 * after it. Return TRUE on success, or FALSE if there are no more open
273 * file buffers. */
274 bool close_buffer(void)
276 assert(openfile != NULL);
278 /* If only one file buffer is open, get out. */
279 if (openfile == openfile->next)
280 return FALSE;
282 /* Switch to the next file buffer. */
283 switch_to_next_buffer_void();
285 /* Close the file buffer we had open before. */
286 unlink_opennode(openfile->prev);
288 display_main_list();
290 return TRUE;
292 #endif /* ENABLE_MULTIBUFFER */
294 /* A bit of a copy and paste from open_file(), is_file_writable()
295 * just checks whether the file is appendable as a quick
296 * permissions check, and we tend to err on the side of permissiveness
297 * (reporting TRUE when it might be wrong) to not fluster users
298 * editing on odd filesystems by printing incorrect warnings.
300 int is_file_writable(const char *filename)
302 struct stat fileinfo, fileinfo2;
303 int fd;
304 FILE *f;
305 char *full_filename;
306 bool ans = TRUE;
309 if (ISSET(VIEW_MODE))
310 return TRUE;
312 assert(filename != NULL);
314 /* Get the specified file's full path. */
315 full_filename = get_full_path(filename);
317 /* Okay, if we can't stat the path due to a component's
318 permissions, just try the relative one */
319 if (full_filename == NULL
320 || (stat(full_filename, &fileinfo) == -1 && stat(filename, &fileinfo2) != -1))
321 full_filename = mallocstrcpy(NULL, filename);
323 if ((fd = open(full_filename, O_WRONLY | O_CREAT | O_APPEND, S_IRUSR |
324 S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)) == -1
325 || (f = fdopen(fd, "a")) == NULL)
326 ans = FALSE;
327 else
328 fclose(f);
329 close(fd);
331 free(full_filename);
332 return ans;
335 /* We make a new line of text from buf. buf is length buf_len. If
336 * first_line_ins is TRUE, then we put the new line at the top of the
337 * file. Otherwise, we assume prevnode is the last line of the file,
338 * and put our line after prevnode. */
339 filestruct *read_line(char *buf, filestruct *prevnode, bool
340 *first_line_ins, size_t buf_len)
342 filestruct *fileptr = (filestruct *)nmalloc(sizeof(filestruct));
344 /* Convert nulls to newlines. buf_len is the string's real
345 * length. */
346 unsunder(buf, buf_len);
348 assert(openfile->fileage != NULL && strlen(buf) == buf_len);
350 fileptr->data = mallocstrcpy(NULL, buf);
352 #ifndef NANO_TINY
353 /* If it's a DOS file ("\r\n"), and file conversion isn't disabled,
354 * strip the '\r' part from fileptr->data. */
355 if (!ISSET(NO_CONVERT) && buf_len > 0 && buf[buf_len - 1] == '\r')
356 fileptr->data[buf_len - 1] = '\0';
357 #endif
359 #ifdef ENABLE_COLOR
360 fileptr->multidata = NULL;
361 #endif
363 if (*first_line_ins) {
364 /* Special case: We're inserting with the cursor on the first
365 * line. */
366 fileptr->prev = NULL;
367 fileptr->next = openfile->fileage;
368 fileptr->lineno = 1;
369 if (*first_line_ins) {
370 *first_line_ins = FALSE;
371 /* If we're inserting into the first line of the file, then
372 * we want to make sure that our edit buffer stays on the
373 * first line and that fileage stays up to date. */
374 openfile->edittop = fileptr;
375 } else
376 openfile->filebot = fileptr;
377 openfile->fileage = fileptr;
378 } else {
379 assert(prevnode != NULL);
381 fileptr->prev = prevnode;
382 fileptr->next = NULL;
383 fileptr->lineno = prevnode->lineno + 1;
384 prevnode->next = fileptr;
387 return fileptr;
390 /* Read an open file into the current buffer. f should be set to the
391 * open file, and filename should be set to the name of the file.
392 * undoable means do we want to create undo records to try and undo this.
393 * Will also attempt to check file writability if fd > 0 and checkwritable == TRUE
395 void read_file(FILE *f, int fd, const char *filename, bool undoable, bool checkwritable)
397 size_t num_lines = 0;
398 /* The number of lines in the file. */
399 size_t len = 0;
400 /* The length of the current line of the file. */
401 size_t i = 0;
402 /* The position in the current line of the file. */
403 size_t bufx = MAX_BUF_SIZE;
404 /* The size of each chunk of the file that we read. */
405 char input = '\0';
406 /* The current input character. */
407 char *buf;
408 /* The buffer where we store chunks of the file. */
409 filestruct *fileptr = openfile->current;
410 /* The current line of the file. */
411 bool first_line_ins = FALSE;
412 /* Whether we're inserting with the cursor on the first line. */
413 int input_int;
414 /* The current value we read from the file, whether an input
415 * character or EOF. */
416 bool writable = TRUE;
417 /* Is the file writable (if we care) */
418 #ifndef NANO_TINY
419 int format = 0;
420 /* 0 = *nix, 1 = DOS, 2 = Mac, 3 = both DOS and Mac. */
421 #endif
423 assert(openfile->fileage != NULL && openfile->current != NULL);
425 buf = charalloc(bufx);
426 buf[0] = '\0';
428 #ifndef NANO_TINY
429 if (undoable)
430 add_undo(INSERT);
431 #endif
433 if (openfile->current == openfile->fileage)
434 first_line_ins = TRUE;
435 else
436 fileptr = openfile->current->prev;
438 /* Read the entire file into the filestruct. */
439 while ((input_int = getc(f)) != EOF) {
440 input = (char)input_int;
442 /* If it's a *nix file ("\n") or a DOS file ("\r\n"), and file
443 * conversion isn't disabled, handle it! */
444 if (input == '\n') {
445 #ifndef NANO_TINY
446 /* If it's a DOS file or a DOS/Mac file ('\r' before '\n' on
447 * the first line if we think it's a *nix file, or on any
448 * line otherwise), and file conversion isn't disabled,
449 * handle it! */
450 if (!ISSET(NO_CONVERT) && (num_lines == 0 || format != 0) &&
451 i > 0 && buf[i - 1] == '\r') {
452 if (format == 0 || format == 2)
453 format++;
455 #endif
457 /* Read in the line properly. */
458 fileptr = read_line(buf, fileptr, &first_line_ins, len);
460 /* Reset the line length in preparation for the next
461 * line. */
462 len = 0;
464 num_lines++;
465 buf[0] = '\0';
466 i = 0;
467 #ifndef NANO_TINY
468 /* If it's a Mac file ('\r' without '\n' on the first line if we
469 * think it's a *nix file, or on any line otherwise), and file
470 * conversion isn't disabled, handle it! */
471 } else if (!ISSET(NO_CONVERT) && (num_lines == 0 ||
472 format != 0) && i > 0 && buf[i - 1] == '\r') {
473 /* If we currently think the file is a *nix file, set format
474 * to Mac. If we currently think the file is a DOS file,
475 * set format to both DOS and Mac. */
476 if (format == 0 || format == 1)
477 format += 2;
479 /* Read in the line properly. */
480 fileptr = read_line(buf, fileptr, &first_line_ins, len);
482 /* Reset the line length in preparation for the next line.
483 * Since we've already read in the next character, reset it
484 * to 1 instead of 0. */
485 len = 1;
487 num_lines++;
488 buf[0] = input;
489 buf[1] = '\0';
490 i = 1;
491 #endif
492 } else {
493 /* Calculate the total length of the line. It might have
494 * nulls in it, so we can't just use strlen() here. */
495 len++;
497 /* Now we allocate a bigger buffer MAX_BUF_SIZE characters
498 * at a time. If we allocate a lot of space for one line,
499 * we may indeed have to use a buffer this big later on, so
500 * we don't decrease it at all. We do free it at the end,
501 * though. */
502 if (i >= bufx - 1) {
503 bufx += MAX_BUF_SIZE;
504 buf = charealloc(buf, bufx);
507 buf[i] = input;
508 buf[i + 1] = '\0';
509 i++;
513 /* Perhaps this could use some better handling. */
514 if (ferror(f))
515 nperror(filename);
516 fclose(f);
517 if (fd > 0 && checkwritable) {
518 close(fd);
519 writable = is_file_writable(filename);
522 #ifndef NANO_TINY
523 /* If file conversion isn't disabled and the last character in this
524 * file is '\r', read it in properly as a Mac format line. */
525 if (len == 0 && !ISSET(NO_CONVERT) && input == '\r') {
526 len = 1;
528 buf[0] = input;
529 buf[1] = '\0';
531 #endif
533 /* Did we not get a newline and still have stuff to do? */
534 if (len > 0) {
535 #ifndef NANO_TINY
536 /* If file conversion isn't disabled and the last character in
537 * this file is '\r', set format to Mac if we currently think
538 * the file is a *nix file, or to both DOS and Mac if we
539 * currently think the file is a DOS file. */
540 if (!ISSET(NO_CONVERT) && buf[len - 1] == '\r' &&
541 (format == 0 || format == 1))
542 format += 2;
543 #endif
545 /* Read in the last line properly. */
546 fileptr = read_line(buf, fileptr, &first_line_ins, len);
547 num_lines++;
550 free(buf);
552 /* If we didn't get a file and we don't already have one, open a
553 * blank buffer. */
554 if (fileptr == NULL)
555 open_buffer("", FALSE);
557 /* Attach the file we got to the filestruct. If we got a file of
558 * zero bytes, don't do anything. */
559 if (num_lines > 0) {
560 /* If the file we got doesn't end in a newline, tack its last
561 * line onto the beginning of the line at current. */
562 if (len > 0) {
563 size_t current_len = strlen(openfile->current->data);
565 /* Adjust the current x-coordinate to compensate for the
566 * change in the current line. */
567 if (num_lines == 1)
568 openfile->current_x += len;
569 else
570 openfile->current_x = len;
572 /* Tack the text at fileptr onto the beginning of the text
573 * at current. */
574 openfile->current->data =
575 charealloc(openfile->current->data, len +
576 current_len + 1);
577 charmove(openfile->current->data + len,
578 openfile->current->data, current_len + 1);
579 strncpy(openfile->current->data, fileptr->data, len);
581 /* Don't destroy fileage, edittop, or filebot! */
582 if (fileptr == openfile->fileage)
583 openfile->fileage = openfile->current;
584 if (fileptr == openfile->edittop)
585 openfile->edittop = openfile->current;
586 if (fileptr == openfile->filebot)
587 openfile->filebot = openfile->current;
589 /* Move fileptr back one line and blow away the old fileptr,
590 * since its text has been saved. */
591 fileptr = fileptr->prev;
592 if (fileptr != NULL) {
593 if (fileptr->next != NULL)
594 free(fileptr->next);
598 /* Attach the line at current after the line at fileptr. */
599 if (fileptr != NULL) {
600 fileptr->next = openfile->current;
601 openfile->current->prev = fileptr;
604 /* Renumber starting with the last line of the file we
605 * inserted. */
606 renumber(openfile->current);
609 openfile->totsize += get_totsize(openfile->fileage,
610 openfile->filebot);
612 /* If the NO_NEWLINES flag isn't set, and text has been added to
613 * the magicline (i.e. a file that doesn't end in a newline has been
614 * inserted at the end of the current buffer), add a new magicline,
615 * and move the current line down to it. */
616 if (!ISSET(NO_NEWLINES) && openfile->filebot->data[0] != '\0') {
617 new_magicline();
618 openfile->current = openfile->filebot;
619 openfile->current_x = 0;
622 /* Set the current place we want to the end of the last line of the
623 * file we inserted. */
624 openfile->placewewant = xplustabs();
626 #ifndef NANO_TINY
627 if (undoable)
628 update_undo(INSERT);
630 if (format == 3) {
631 if (writable)
632 statusbar(
633 P_("Read %lu line (Converted from DOS and Mac format)",
634 "Read %lu lines (Converted from DOS and Mac format)",
635 (unsigned long)num_lines), (unsigned long)num_lines);
636 else
637 statusbar(
638 P_("Read %lu line (Converted from DOS and Mac format - Warning: No write permission)",
639 "Read %lu lines (Converted from DOS and Mac format - Warning: No write permission)",
640 (unsigned long)num_lines), (unsigned long)num_lines);
641 } else if (format == 2) {
642 openfile->fmt = MAC_FILE;
643 if (writable)
644 statusbar(P_("Read %lu line (Converted from Mac format)",
645 "Read %lu lines (Converted from Mac format)",
646 (unsigned long)num_lines), (unsigned long)num_lines);
647 else
648 statusbar(P_("Read %lu line (Converted from Mac format - Warning: No write permission)",
649 "Read %lu lines (Converted from Mac format - Warning: No write permission)",
650 (unsigned long)num_lines), (unsigned long)num_lines);
651 } else if (format == 1) {
652 openfile->fmt = DOS_FILE;
653 if (writable)
654 statusbar(P_("Read %lu line (Converted from DOS format)",
655 "Read %lu lines (Converted from DOS format)",
656 (unsigned long)num_lines), (unsigned long)num_lines);
657 else
658 statusbar(P_("Read %lu line (Converted from DOS format - Warning: No write permission)",
659 "Read %lu lines (Converted from DOS format - Warning: No write permission)",
660 (unsigned long)num_lines), (unsigned long)num_lines);
661 } else
662 #endif
663 if (writable)
664 statusbar(P_("Read %lu line", "Read %lu lines",
665 (unsigned long)num_lines), (unsigned long)num_lines);
666 else
667 statusbar(P_("Read %lu line ( Warning: No write permission)",
668 "Read %lu lines (Warning: No write permission)",
669 (unsigned long)num_lines), (unsigned long)num_lines);
672 /* Open the file (and decide if it exists). If newfie is TRUE, display
673 * "New File" if the file is missing. Otherwise, say "[filename] not
674 * found".
676 * Return -2 if we say "New File", -1 if the file isn't opened, and the
677 * fd opened otherwise. The file might still have an error while reading
678 * with a 0 return value. *f is set to the opened file. */
679 int open_file(const char *filename, bool newfie, FILE **f)
681 struct stat fileinfo, fileinfo2;
682 int fd;
683 char *full_filename;
685 assert(filename != NULL && f != NULL);
687 /* Get the specified file's full path. */
688 full_filename = get_full_path(filename);
690 /* Okay, if we can't stat the path due to a component's
691 permissions, just try the relative one */
692 if (full_filename == NULL
693 || (stat(full_filename, &fileinfo) == -1 && stat(filename, &fileinfo2) != -1))
694 full_filename = mallocstrcpy(NULL, filename);
696 if (stat(full_filename, &fileinfo) == -1) {
697 /* Well, maybe we can open the file even if the OS
698 says its not there */
699 if ((fd = open(filename, O_RDONLY)) != -1) {
700 statusbar(_("Reading File"));
701 free(full_filename);
702 return 0;
705 if (newfie) {
706 statusbar(_("New File"));
707 return -2;
709 statusbar(_("\"%s\" not found"), filename);
710 beep();
711 return -1;
712 } else if (S_ISDIR(fileinfo.st_mode) || S_ISCHR(fileinfo.st_mode) ||
713 S_ISBLK(fileinfo.st_mode)) {
714 /* Don't open directories, character files, or block files.
715 * Sorry, /dev/sndstat! */
716 statusbar(S_ISDIR(fileinfo.st_mode) ?
717 _("\"%s\" is a directory") :
718 _("\"%s\" is a device file"), filename);
719 beep();
720 return -1;
721 } else if ((fd = open(full_filename, O_RDONLY)) == -1) {
722 statusbar(_("Error reading %s: %s"), filename,
723 strerror(errno));
724 beep();
725 return -1;
726 } else {
727 /* The file is A-OK. Open it. */
728 *f = fdopen(fd, "rb");
730 if (*f == NULL) {
731 statusbar(_("Error reading %s: %s"), filename,
732 strerror(errno));
733 beep();
734 close(fd);
735 } else
736 statusbar(_("Reading File"));
739 free(full_filename);
741 return fd;
744 /* This function will return the name of the first available extension
745 * of a filename (starting with [name][suffix], then [name][suffix].1,
746 * etc.). Memory is allocated for the return value. If no writable
747 * extension exists, we return "". */
748 char *get_next_filename(const char *name, const char *suffix)
750 static int ulmax_digits = -1;
751 unsigned long i = 0;
752 char *buf;
753 size_t namelen, suffixlen;
755 assert(name != NULL && suffix != NULL);
757 if (ulmax_digits == -1)
758 ulmax_digits = digits(ULONG_MAX);
760 namelen = strlen(name);
761 suffixlen = strlen(suffix);
763 buf = charalloc(namelen + suffixlen + ulmax_digits + 2);
764 sprintf(buf, "%s%s", name, suffix);
766 while (TRUE) {
767 struct stat fs;
769 if (stat(buf, &fs) == -1)
770 return buf;
771 if (i == ULONG_MAX)
772 break;
774 i++;
775 sprintf(buf + namelen + suffixlen, ".%lu", i);
778 /* We get here only if there is no possible save file. Blank out
779 * the filename to indicate this. */
780 null_at(&buf, 0);
782 return buf;
785 /* Insert a file into a new buffer if the MULTIBUFFER flag is set, or
786 * into the current buffer if it isn't. If execute is TRUE, insert the
787 * output of an executed command instead of a file. */
788 void do_insertfile(
789 #ifndef NANO_TINY
790 bool execute
791 #else
792 void
793 #endif
796 int i;
797 const char *msg;
798 char *ans = mallocstrcpy(NULL, "");
799 /* The last answer the user typed at the statusbar prompt. */
800 filestruct *edittop_save = openfile->edittop;
801 size_t current_x_save = openfile->current_x;
802 ssize_t current_y_save = openfile->current_y;
803 bool edittop_inside = FALSE, meta_key = FALSE, func_key = FALSE;
804 const sc *s;
805 #ifndef NANO_TINY
806 bool right_side_up = FALSE, single_line = FALSE;
807 #endif
809 currmenu = MINSERTFILE;
811 while (TRUE) {
812 #ifndef NANO_TINY
813 if (execute) {
814 msg =
815 #ifdef ENABLE_MULTIBUFFER
816 ISSET(MULTIBUFFER) ?
817 _("Command to execute in new buffer [from %s] ") :
818 #endif
819 _("Command to execute [from %s] ");
820 } else {
821 #endif
822 msg =
823 #ifdef ENABLE_MULTIBUFFER
824 ISSET(MULTIBUFFER) ?
825 _("File to insert into new buffer [from %s] ") :
826 #endif
827 _("File to insert [from %s] ");
828 #ifndef NANO_TINY
830 #endif
832 i = do_prompt(TRUE,
833 #ifndef DISABLE_TABCOMP
834 TRUE,
835 #endif
836 #ifndef NANO_TINY
837 execute ? MEXTCMD :
838 #endif
839 MINSERTFILE, ans,
840 &meta_key, &func_key,
841 #ifndef NANO_TINY
842 NULL,
843 #endif
844 edit_refresh, msg,
845 #ifndef DISABLE_OPERATINGDIR
846 operating_dir != NULL && strcmp(operating_dir,
847 ".") != 0 ? operating_dir :
848 #endif
849 "./");
851 /* If we're in multibuffer mode and the filename or command is
852 * blank, open a new buffer instead of canceling. If the
853 * filename or command begins with a newline (i.e. an encoded
854 * null), treat it as though it's blank. */
855 if (i == -1 || ((i == -2 || *answer == '\n')
856 #ifdef ENABLE_MULTIBUFFER
857 && !ISSET(MULTIBUFFER)
858 #endif
859 )) {
860 statusbar(_("Cancelled"));
861 break;
862 } else {
863 size_t pww_save = openfile->placewewant;
865 ans = mallocstrcpy(ans, answer);
867 s = get_shortcut(currmenu, &i, &meta_key, &func_key);
869 #ifndef NANO_TINY
870 #ifdef ENABLE_MULTIBUFFER
872 if (s && s->scfunc == NEW_BUFFER_MSG) {
873 /* Don't allow toggling if we're in view mode. */
874 if (!ISSET(VIEW_MODE))
875 TOGGLE(MULTIBUFFER);
876 continue;
877 } else
878 #endif
879 if (s && s->scfunc == EXT_CMD_MSG) {
880 execute = !execute;
881 continue;
883 #ifndef DISABLE_BROWSER
884 else
885 #endif
886 #endif /* !NANO_TINY */
888 #ifndef DISABLE_BROWSER
889 if (s && s->scfunc == TO_FILES_MSG) {
890 char *tmp = do_browse_from(answer);
892 if (tmp == NULL)
893 continue;
895 /* We have a file now. Indicate this. */
896 free(answer);
897 answer = tmp;
899 i = 0;
901 #endif
903 /* If we don't have a file yet, go back to the statusbar
904 * prompt. */
905 if (i != 0
906 #ifdef ENABLE_MULTIBUFFER
907 && (i != -2 || !ISSET(MULTIBUFFER))
908 #endif
910 continue;
912 #ifndef NANO_TINY
913 /* Keep track of whether the mark begins inside the
914 * partition and will need adjustment. */
915 if (openfile->mark_set) {
916 filestruct *top, *bot;
917 size_t top_x, bot_x;
919 mark_order((const filestruct **)&top, &top_x,
920 (const filestruct **)&bot, &bot_x,
921 &right_side_up);
923 single_line = (top == bot);
925 #endif
927 #ifdef ENABLE_MULTIBUFFER
928 if (!ISSET(MULTIBUFFER)) {
929 #endif
930 /* If we're not inserting into a new buffer, partition
931 * the filestruct so that it contains no text and hence
932 * looks like a new buffer, and keep track of whether
933 * the top of the edit window is inside the
934 * partition. */
935 filepart = partition_filestruct(openfile->current,
936 openfile->current_x, openfile->current,
937 openfile->current_x);
938 edittop_inside =
939 (openfile->edittop == openfile->fileage);
940 #ifdef ENABLE_MULTIBUFFER
942 #endif
944 /* Convert newlines to nulls, just before we insert the file
945 * or execute the command. */
946 sunder(answer);
947 align(&answer);
949 #ifndef NANO_TINY
950 if (execute) {
951 #ifdef ENABLE_MULTIBUFFER
952 if (ISSET(MULTIBUFFER))
953 /* Open a blank buffer. */
954 open_buffer("", FALSE);
955 #endif
957 /* Save the command's output in the current buffer. */
958 execute_command(answer);
960 #ifdef ENABLE_MULTIBUFFER
961 if (ISSET(MULTIBUFFER)) {
962 /* Move back to the beginning of the first line of
963 * the buffer. */
964 openfile->current = openfile->fileage;
965 openfile->current_x = 0;
966 openfile->placewewant = 0;
968 #endif
969 } else {
970 #endif /* !NANO_TINY */
971 /* Make sure the path to the file specified in answer is
972 * tilde-expanded. */
973 answer = mallocstrassn(answer,
974 real_dir_from_tilde(answer));
976 /* Save the file specified in answer in the current
977 * buffer. */
978 open_buffer(answer, TRUE);
979 #ifndef NANO_TINY
981 #endif
983 #ifdef ENABLE_MULTIBUFFER
984 if (ISSET(MULTIBUFFER))
985 /* Update the screen to account for the current
986 * buffer. */
987 display_buffer();
988 else
989 #endif
991 filestruct *top_save = openfile->fileage;
993 /* If we were at the top of the edit window before, set
994 * the saved value of edittop to the new top of the edit
995 * window. */
996 if (edittop_inside)
997 edittop_save = openfile->fileage;
999 /* Update the current x-coordinate to account for the
1000 * number of characters inserted on the current line.
1001 * If the mark begins inside the partition, adjust the
1002 * mark coordinates to compensate for the change in the
1003 * current line. */
1004 openfile->current_x = strlen(openfile->filebot->data);
1005 if (openfile->fileage == openfile->filebot) {
1006 #ifndef NANO_TINY
1007 if (openfile->mark_set) {
1008 openfile->mark_begin = openfile->current;
1009 if (!right_side_up)
1010 openfile->mark_begin_x +=
1011 openfile->current_x;
1013 #endif
1014 openfile->current_x += current_x_save;
1016 #ifndef NANO_TINY
1017 else if (openfile->mark_set) {
1018 if (!right_side_up) {
1019 if (single_line) {
1020 openfile->mark_begin = openfile->current;
1021 openfile->mark_begin_x -= current_x_save;
1022 } else
1023 openfile->mark_begin_x -=
1024 openfile->current_x;
1027 #endif
1029 /* Update the current y-coordinate to account for the
1030 * number of lines inserted. */
1031 openfile->current_y += current_y_save;
1033 /* Unpartition the filestruct so that it contains all
1034 * the text again. Note that we've replaced the
1035 * non-text originally in the partition with the text in
1036 * the inserted file/executed command output. */
1037 unpartition_filestruct(&filepart);
1039 /* Renumber starting with the beginning line of the old
1040 * partition. */
1041 renumber(top_save);
1043 /* Restore the old edittop. */
1044 openfile->edittop = edittop_save;
1046 /* Restore the old place we want. */
1047 openfile->placewewant = pww_save;
1049 /* Mark the file as modified. */
1050 set_modified();
1052 /* Update the screen. */
1053 edit_refresh();
1056 break;
1059 shortcut_init(FALSE);
1061 free(ans);
1064 /* Insert a file into a new buffer or the current buffer, depending on
1065 * whether the MULTIBUFFER flag is set. If we're in view mode, only
1066 * allow inserting a file into a new buffer. */
1067 void do_insertfile_void(void)
1070 if (ISSET(RESTRICTED)) {
1071 nano_disabled_msg();
1072 return;
1075 #ifdef ENABLE_MULTIBUFFER
1076 if (ISSET(VIEW_MODE) && !ISSET(MULTIBUFFER))
1077 statusbar(_("Key invalid in non-multibuffer mode"));
1078 else
1079 #endif
1080 do_insertfile(
1081 #ifndef NANO_TINY
1082 FALSE
1083 #endif
1086 display_main_list();
1089 /* When passed "[relative path]" or "[relative path][filename]" in
1090 * origpath, return "[full path]" or "[full path][filename]" on success,
1091 * or NULL on error. Do this if the file doesn't exist but the relative
1092 * path does, since the file could exist in memory but not yet on disk).
1093 * Don't do this if the relative path doesn't exist, since we won't be
1094 * able to go there. */
1095 char *get_full_path(const char *origpath)
1097 struct stat fileinfo;
1098 char *d_here, *d_there, *d_there_file = NULL;
1099 const char *last_slash;
1100 bool path_only;
1102 if (origpath == NULL)
1103 return NULL;
1105 /* Get the current directory. If it doesn't exist, back up and try
1106 * again until we get a directory that does, and use that as the
1107 * current directory. */
1108 d_here = charalloc(PATH_MAX + 1);
1109 d_here = getcwd(d_here, PATH_MAX + 1);
1111 while (d_here == NULL) {
1112 if (chdir("..") == -1)
1113 break;
1115 d_here = getcwd(d_here, PATH_MAX + 1);
1118 /* If we succeeded, canonicalize it in d_here. */
1119 if (d_here != NULL) {
1120 align(&d_here);
1122 /* If the current directory isn't "/", tack a slash onto the end
1123 * of it. */
1124 if (strcmp(d_here, "/") != 0) {
1125 d_here = charealloc(d_here, strlen(d_here) + 2);
1126 strcat(d_here, "/");
1128 /* Otherwise, set d_here to "". */
1129 } else
1130 d_here = mallocstrcpy(NULL, "");
1132 d_there = real_dir_from_tilde(origpath);
1134 /* If stat()ing d_there fails, assume that d_there refers to a new
1135 * file that hasn't been saved to disk yet. Set path_only to TRUE
1136 * if d_there refers to a directory, and FALSE otherwise. */
1137 path_only = (stat(d_there, &fileinfo) != -1 &&
1138 S_ISDIR(fileinfo.st_mode));
1140 /* If path_only is TRUE, make sure d_there ends in a slash. */
1141 if (path_only) {
1142 size_t d_there_len = strlen(d_there);
1144 if (d_there[d_there_len - 1] != '/') {
1145 d_there = charealloc(d_there, d_there_len + 2);
1146 strcat(d_there, "/");
1150 /* Search for the last slash in d_there. */
1151 last_slash = strrchr(d_there, '/');
1153 /* If we didn't find one, then make sure the answer is in the format
1154 * "d_here/d_there". */
1155 if (last_slash == NULL) {
1156 assert(!path_only);
1158 d_there_file = d_there;
1159 d_there = d_here;
1160 } else {
1161 /* If path_only is FALSE, then save the filename portion of the
1162 * answer (everything after the last slash) in d_there_file. */
1163 if (!path_only)
1164 d_there_file = mallocstrcpy(NULL, last_slash + 1);
1166 /* Remove the filename portion of the answer from d_there. */
1167 null_at(&d_there, last_slash - d_there + 1);
1169 /* Go to the path specified in d_there. */
1170 if (chdir(d_there) == -1) {
1171 free(d_there);
1172 d_there = NULL;
1173 } else {
1174 free(d_there);
1176 /* Get the full path. */
1177 d_there = charalloc(PATH_MAX + 1);
1178 d_there = getcwd(d_there, PATH_MAX + 1);
1180 /* If we succeeded, canonicalize it in d_there. */
1181 if (d_there != NULL) {
1182 align(&d_there);
1184 /* If the current directory isn't "/", tack a slash onto
1185 * the end of it. */
1186 if (strcmp(d_there, "/") != 0) {
1187 d_there = charealloc(d_there, strlen(d_there) + 2);
1188 strcat(d_there, "/");
1190 } else
1191 /* Otherwise, set path_only to TRUE, so that we clean up
1192 * correctly, free all allocated memory, and return
1193 * NULL. */
1194 path_only = TRUE;
1196 /* Finally, go back to the path specified in d_here,
1197 * where we were before. We don't check for a chdir()
1198 * error, since we can do nothing if we get one. */
1199 IGNORE_CALL_RESULT(chdir(d_here));
1201 /* Free d_here, since we're done using it. */
1202 free(d_here);
1206 /* At this point, if path_only is FALSE and d_there isn't NULL,
1207 * d_there contains the path portion of the answer and d_there_file
1208 * contains the filename portion of the answer. If this is the
1209 * case, tack the latter onto the end of the former. d_there will
1210 * then contain the complete answer. */
1211 if (!path_only && d_there != NULL) {
1212 d_there = charealloc(d_there, strlen(d_there) +
1213 strlen(d_there_file) + 1);
1214 strcat(d_there, d_there_file);
1217 /* Free d_there_file, since we're done using it. */
1218 if (d_there_file != NULL)
1219 free(d_there_file);
1221 return d_there;
1224 /* Return the full version of path, as returned by get_full_path(). On
1225 * error, if path doesn't reference a directory, or if the directory
1226 * isn't writable, return NULL. */
1227 char *check_writable_directory(const char *path)
1229 char *full_path = get_full_path(path);
1231 /* If get_full_path() fails, return NULL. */
1232 if (full_path == NULL)
1233 return NULL;
1235 /* If we can't write to path or path isn't a directory, return
1236 * NULL. */
1237 if (access(full_path, W_OK) != 0 ||
1238 full_path[strlen(full_path) - 1] != '/') {
1239 free(full_path);
1240 return NULL;
1243 /* Otherwise, return the full path. */
1244 return full_path;
1247 /* This function calls mkstemp(($TMPDIR|P_tmpdir|/tmp/)"nano.XXXXXX").
1248 * On success, it returns the malloc()ed filename and corresponding FILE
1249 * stream, opened in "r+b" mode. On error, it returns NULL for the
1250 * filename and leaves the FILE stream unchanged. */
1251 char *safe_tempfile(FILE **f)
1253 char *full_tempdir = NULL;
1254 const char *tmpdir_env;
1255 int fd;
1256 mode_t original_umask = 0;
1258 assert(f != NULL);
1260 /* If $TMPDIR is set, set tempdir to it, run it through
1261 * get_full_path(), and save the result in full_tempdir. Otherwise,
1262 * leave full_tempdir set to NULL. */
1263 tmpdir_env = getenv("TMPDIR");
1264 if (tmpdir_env != NULL)
1265 full_tempdir = check_writable_directory(tmpdir_env);
1267 /* If $TMPDIR is unset, empty, or not a writable directory, and
1268 * full_tempdir is NULL, try P_tmpdir instead. */
1269 if (full_tempdir == NULL)
1270 full_tempdir = check_writable_directory(P_tmpdir);
1272 /* if P_tmpdir is NULL, use /tmp. */
1273 if (full_tempdir == NULL)
1274 full_tempdir = mallocstrcpy(NULL, "/tmp/");
1276 full_tempdir = charealloc(full_tempdir, strlen(full_tempdir) + 12);
1277 strcat(full_tempdir, "nano.XXXXXX");
1279 original_umask = umask(0);
1280 umask(S_IRWXG | S_IRWXO);
1282 fd = mkstemp(full_tempdir);
1284 if (fd != -1)
1285 *f = fdopen(fd, "r+b");
1286 else {
1287 free(full_tempdir);
1288 full_tempdir = NULL;
1291 umask(original_umask);
1293 return full_tempdir;
1296 #ifndef DISABLE_OPERATINGDIR
1297 /* Initialize full_operating_dir based on operating_dir. */
1298 void init_operating_dir(void)
1300 assert(full_operating_dir == NULL);
1302 if (operating_dir == NULL)
1303 return;
1305 full_operating_dir = get_full_path(operating_dir);
1307 /* If get_full_path() failed or the operating directory is
1308 * inaccessible, unset operating_dir. */
1309 if (full_operating_dir == NULL || chdir(full_operating_dir) == -1) {
1310 free(full_operating_dir);
1311 full_operating_dir = NULL;
1312 free(operating_dir);
1313 operating_dir = NULL;
1317 /* Check to see if we're inside the operating directory. Return FALSE
1318 * if we are, or TRUE otherwise. If allow_tabcomp is TRUE, allow
1319 * incomplete names that would be matches for the operating directory,
1320 * so that tab completion will work. */
1321 bool check_operating_dir(const char *currpath, bool allow_tabcomp)
1323 /* full_operating_dir is global for memory cleanup. It should have
1324 * already been initialized by init_operating_dir(). Also, a
1325 * relative operating directory path will only be handled properly
1326 * if this is done. */
1328 char *fullpath;
1329 bool retval = FALSE;
1330 const char *whereami1, *whereami2 = NULL;
1332 /* If no operating directory is set, don't bother doing anything. */
1333 if (operating_dir == NULL)
1334 return FALSE;
1336 assert(full_operating_dir != NULL);
1338 fullpath = get_full_path(currpath);
1340 /* If fullpath is NULL, it means some directory in the path doesn't
1341 * exist or is unreadable. If allow_tabcomp is FALSE, then currpath
1342 * is what the user typed somewhere. We don't want to report a
1343 * non-existent directory as being outside the operating directory,
1344 * so we return FALSE. If allow_tabcomp is TRUE, then currpath
1345 * exists, but is not executable. So we say it isn't in the
1346 * operating directory. */
1347 if (fullpath == NULL)
1348 return allow_tabcomp;
1350 whereami1 = strstr(fullpath, full_operating_dir);
1351 if (allow_tabcomp)
1352 whereami2 = strstr(full_operating_dir, fullpath);
1354 /* If both searches failed, we're outside the operating directory.
1355 * Otherwise, check the search results. If the full operating
1356 * directory path is not at the beginning of the full current path
1357 * (for normal usage) and vice versa (for tab completion, if we're
1358 * allowing it), we're outside the operating directory. */
1359 if (whereami1 != fullpath && whereami2 != full_operating_dir)
1360 retval = TRUE;
1361 free(fullpath);
1363 /* Otherwise, we're still inside it. */
1364 return retval;
1366 #endif
1368 #ifndef NANO_TINY
1369 void init_backup_dir(void)
1371 char *full_backup_dir;
1373 if (backup_dir == NULL)
1374 return;
1376 full_backup_dir = get_full_path(backup_dir);
1378 /* If get_full_path() failed or the backup directory is
1379 * inaccessible, unset backup_dir. */
1380 if (full_backup_dir == NULL ||
1381 full_backup_dir[strlen(full_backup_dir) - 1] != '/') {
1382 free(full_backup_dir);
1383 free(backup_dir);
1384 backup_dir = NULL;
1385 } else {
1386 free(backup_dir);
1387 backup_dir = full_backup_dir;
1390 #endif
1392 /* Read from inn, write to out. We assume inn is opened for reading,
1393 * and out for writing. We return 0 on success, -1 on read error, or -2
1394 * on write error. */
1395 int copy_file(FILE *inn, FILE *out)
1397 int retval = 0;
1398 char buf[BUFSIZ];
1399 size_t charsread;
1401 assert(inn != NULL && out != NULL && inn != out);
1403 do {
1404 charsread = fread(buf, sizeof(char), BUFSIZ, inn);
1405 if (charsread == 0 && ferror(inn)) {
1406 retval = -1;
1407 break;
1409 if (fwrite(buf, sizeof(char), charsread, out) < charsread) {
1410 retval = -2;
1411 break;
1413 } while (charsread > 0);
1415 if (fclose(inn) == EOF)
1416 retval = -1;
1417 if (fclose(out) == EOF)
1418 retval = -2;
1420 return retval;
1423 /* Write a file out to disk. If f_open isn't NULL, we assume that it is
1424 * a stream associated with the file, and we don't try to open it
1425 * ourselves. If tmp is TRUE, we set the umask to disallow anyone else
1426 * from accessing the file, we don't set the filename to its name, and
1427 * we don't print out how many lines we wrote on the statusbar.
1429 * tmp means we are writing a temporary file in a secure fashion. We
1430 * use it when spell checking or dumping the file on an error. If
1431 * append is APPEND, it means we are appending instead of overwriting.
1432 * If append is PREPEND, it means we are prepending instead of
1433 * overwriting. If nonamechange is TRUE, we don't change the current
1434 * filename. nonamechange is ignored if tmp is FALSE, we're appending,
1435 * or we're prepending.
1437 * Return TRUE on success or FALSE on error. */
1438 bool write_file(const char *name, FILE *f_open, bool tmp, append_type
1439 append, bool nonamechange)
1441 bool retval = FALSE;
1442 /* Instead of returning in this function, you should always
1443 * set retval and then goto cleanup_and_exit. */
1444 size_t lineswritten = 0;
1445 const filestruct *fileptr = openfile->fileage;
1446 int fd;
1447 /* The file descriptor we use. */
1448 mode_t original_umask = 0;
1449 /* Our umask, from when nano started. */
1450 bool realexists;
1451 /* The result of stat(). TRUE if the file exists, FALSE
1452 * otherwise. If name is a link that points nowhere, realexists
1453 * is FALSE. */
1454 struct stat st;
1455 /* The status fields filled in by stat(). */
1456 bool anyexists;
1457 /* The result of lstat(). The same as realexists, unless name
1458 * is a link. */
1459 struct stat lst;
1460 /* The status fields filled in by lstat(). */
1461 char *realname;
1462 /* name after tilde expansion. */
1463 FILE *f = NULL;
1464 /* The actual file, realname, we are writing to. */
1465 char *tempname = NULL;
1466 /* The temp file name we write to on prepend. */
1467 int backup_cflags;
1469 assert(name != NULL);
1471 if (*name == '\0')
1472 return -1;
1474 if (f_open != NULL)
1475 f = f_open;
1477 if (!tmp)
1478 titlebar(NULL);
1480 realname = real_dir_from_tilde(name);
1482 #ifndef DISABLE_OPERATINGDIR
1483 /* If we're writing a temporary file, we're probably going outside
1484 * the operating directory, so skip the operating directory test. */
1485 if (!tmp && check_operating_dir(realname, FALSE)) {
1486 statusbar(_("Can't write outside of %s"), operating_dir);
1487 goto cleanup_and_exit;
1489 #endif
1491 anyexists = (lstat(realname, &lst) != -1);
1493 /* If the temp file exists and isn't already open, give up. */
1494 if (tmp && anyexists && f_open == NULL)
1495 goto cleanup_and_exit;
1497 /* If NOFOLLOW_SYMLINKS is set, it doesn't make sense to prepend or
1498 * append to a symlink. Here we warn about the contradiction. */
1499 if (ISSET(NOFOLLOW_SYMLINKS) && anyexists && S_ISLNK(lst.st_mode)) {
1500 statusbar(
1501 _("Cannot prepend or append to a symlink with --nofollow set"));
1502 goto cleanup_and_exit;
1505 /* Save the state of the file at the end of the symlink (if there is
1506 * one). */
1507 realexists = (stat(realname, &st) != -1);
1509 #ifndef NANO_TINY
1510 /* if we have not stat()d this file before (say, the user just
1511 * specified it interactively), stat and save the value
1512 * or else we will chase null pointers when we do
1513 * modtime checks, preserve file times, etc. during backup */
1514 if (openfile->current_stat == NULL && !tmp && realexists)
1515 stat(realname, openfile->current_stat);
1517 /* We backup only if the backup toggle is set, the file isn't
1518 * temporary, and the file already exists. Furthermore, if we
1519 * aren't appending, prepending, or writing a selection, we backup
1520 * only if the file has not been modified by someone else since nano
1521 * opened it. */
1522 if (ISSET(BACKUP_FILE) && !tmp && realexists && ((append !=
1523 OVERWRITE || openfile->mark_set) || (openfile->current_stat &&
1524 openfile->current_stat->st_mtime == st.st_mtime))) {
1525 int backup_fd;
1526 FILE *backup_file;
1527 char *backupname;
1528 struct utimbuf filetime;
1529 int copy_status;
1531 /* Save the original file's access and modification times. */
1532 filetime.actime = openfile->current_stat->st_atime;
1533 filetime.modtime = openfile->current_stat->st_mtime;
1535 if (f_open == NULL) {
1536 /* Open the original file to copy to the backup. */
1537 f = fopen(realname, "rb");
1539 if (f == NULL) {
1540 statusbar(_("Error reading %s: %s"), realname,
1541 strerror(errno));
1542 beep();
1543 /* If we can't read from the original file, go on, since
1544 * only saving the original file is better than saving
1545 * nothing. */
1546 goto skip_backup;
1550 /* If backup_dir is set, we set backupname to
1551 * backup_dir/backupname~[.number], where backupname is the
1552 * canonicalized absolute pathname of realname with every '/'
1553 * replaced with a '!'. This means that /home/foo/file is
1554 * backed up in backup_dir/!home!foo!file~[.number]. */
1555 if (backup_dir != NULL) {
1556 char *backuptemp = get_full_path(realname);
1558 if (backuptemp == NULL)
1559 /* If get_full_path() failed, we don't have a
1560 * canonicalized absolute pathname, so just use the
1561 * filename portion of the pathname. We use tail() so
1562 * that e.g. ../backupname will be backed up in
1563 * backupdir/backupname~ instead of
1564 * backupdir/../backupname~. */
1565 backuptemp = mallocstrcpy(NULL, tail(realname));
1566 else {
1567 size_t i = 0;
1569 for (; backuptemp[i] != '\0'; i++) {
1570 if (backuptemp[i] == '/')
1571 backuptemp[i] = '!';
1575 backupname = charalloc(strlen(backup_dir) +
1576 strlen(backuptemp) + 1);
1577 sprintf(backupname, "%s%s", backup_dir, backuptemp);
1578 free(backuptemp);
1579 backuptemp = get_next_filename(backupname, "~");
1580 if (*backuptemp == '\0') {
1581 statusbar(_("Error writing backup file %s: %s"), backupname,
1582 _("Too many backup files?"));
1583 free(backuptemp);
1584 free(backupname);
1585 /* If we can't write to the backup, DONT go on, since
1586 whatever caused the backup file to fail (e.g. disk
1587 full may well cause the real file write to fail, which
1588 means we could lose both the backup and the original! */
1589 goto cleanup_and_exit;
1590 } else {
1591 free(backupname);
1592 backupname = backuptemp;
1594 } else {
1595 backupname = charalloc(strlen(realname) + 2);
1596 sprintf(backupname, "%s~", realname);
1599 /* First, unlink any existing backups. Next, open the backup
1600 file with O_CREAT and O_EXCL. If it succeeds, we
1601 have a file descriptor to a new backup file. */
1602 if (unlink(backupname) < 0 && errno != ENOENT && !ISSET(INSECURE_BACKUP)) {
1603 statusbar(_("Error writing backup file %s: %s"), backupname,
1604 strerror(errno));
1605 free(backupname);
1606 goto cleanup_and_exit;
1609 if (ISSET(INSECURE_BACKUP))
1610 backup_cflags = O_WRONLY | O_CREAT | O_APPEND;
1611 else
1612 backup_cflags = O_WRONLY | O_CREAT | O_EXCL | O_APPEND;
1614 backup_fd = open(backupname, backup_cflags,
1615 S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
1616 /* Now we've got a safe file stream. If the previous open()
1617 call failed, this will return NULL. */
1618 backup_file = fdopen(backup_fd, "wb");
1620 if (backup_fd < 0 || backup_file == NULL) {
1621 statusbar(_("Error writing backup file %s: %s"), backupname,
1622 strerror(errno));
1623 free(backupname);
1624 goto cleanup_and_exit;
1627 /* We shouldn't worry about chown()ing something if we're not
1628 root, since it's likely to fail! */
1629 if (geteuid() == NANO_ROOT_UID && fchown(backup_fd,
1630 openfile->current_stat->st_uid, openfile->current_stat->st_gid) == -1
1631 && !ISSET(INSECURE_BACKUP)) {
1632 statusbar(_("Error writing backup file %s: %s"), backupname,
1633 strerror(errno));
1634 free(backupname);
1635 fclose(backup_file);
1636 goto cleanup_and_exit;
1639 if (fchmod(backup_fd, openfile->current_stat->st_mode) == -1 && !ISSET(INSECURE_BACKUP)) {
1640 statusbar(_("Error writing backup file %s: %s"), backupname,
1641 strerror(errno));
1642 free(backupname);
1643 fclose(backup_file);
1644 /* If we can't write to the backup, DONT go on, since
1645 whatever caused the backup file to fail (e.g. disk
1646 full may well cause the real file write to fail, which
1647 means we could lose both the backup and the original! */
1648 goto cleanup_and_exit;
1651 #ifdef DEBUG
1652 fprintf(stderr, "Backing up %s to %s\n", realname, backupname);
1653 #endif
1655 /* Copy the file. */
1656 copy_status = copy_file(f, backup_file);
1658 if (copy_status != 0) {
1659 statusbar(_("Error reading %s: %s"), realname,
1660 strerror(errno));
1661 beep();
1662 goto cleanup_and_exit;
1665 /* And set its metadata. */
1666 if (utime(backupname, &filetime) == -1 && !ISSET(INSECURE_BACKUP)) {
1667 statusbar(_("Error writing backup file %s: %s"), backupname,
1668 strerror(errno));
1669 /* If we can't write to the backup, DONT go on, since
1670 whatever caused the backup file to fail (e.g. disk
1671 full may well cause the real file write to fail, which
1672 means we could lose both the backup and the original! */
1673 goto cleanup_and_exit;
1676 free(backupname);
1679 skip_backup:
1680 #endif /* !NANO_TINY */
1682 /* If NOFOLLOW_SYMLINKS is set and the file is a link, we aren't
1683 * doing prepend or append. So we delete the link first, and just
1684 * overwrite. */
1685 if (ISSET(NOFOLLOW_SYMLINKS) && anyexists && S_ISLNK(lst.st_mode) &&
1686 unlink(realname) == -1) {
1687 statusbar(_("Error writing %s: %s"), realname, strerror(errno));
1688 goto cleanup_and_exit;
1691 if (f_open == NULL) {
1692 original_umask = umask(0);
1694 /* If we create a temp file, we don't let anyone else access it.
1695 * We create a temp file if tmp is TRUE. */
1696 if (tmp)
1697 umask(S_IRWXG | S_IRWXO);
1698 else
1699 umask(original_umask);
1702 /* If we're prepending, copy the file to a temp file. */
1703 if (append == PREPEND) {
1704 int fd_source;
1705 FILE *f_source = NULL;
1707 if (f == NULL) {
1708 f = fopen(realname, "rb");
1710 if (f == NULL) {
1711 statusbar(_("Error reading %s: %s"), realname,
1712 strerror(errno));
1713 beep();
1714 goto cleanup_and_exit;
1718 tempname = safe_tempfile(&f);
1720 if (tempname == NULL) {
1721 statusbar(_("Error writing temp file: %s"),
1722 strerror(errno));
1723 goto cleanup_and_exit;
1726 if (f_open == NULL) {
1727 fd_source = open(realname, O_RDONLY | O_CREAT, S_IRUSR | S_IWUSR);
1729 if (fd_source != -1) {
1730 f_source = fdopen(fd_source, "rb");
1731 if (f_source == NULL) {
1732 statusbar(_("Error reading %s: %s"), realname,
1733 strerror(errno));
1734 beep();
1735 close(fd_source);
1736 fclose(f);
1737 unlink(tempname);
1738 goto cleanup_and_exit;
1743 if (copy_file(f_source, f) != 0) {
1744 statusbar(_("Error writing %s: %s"), tempname,
1745 strerror(errno));
1746 unlink(tempname);
1747 goto cleanup_and_exit;
1751 if (f_open == NULL) {
1752 /* Now open the file in place. Use O_EXCL if tmp is TRUE. This
1753 * is copied from joe, because wiggy says so *shrug*. */
1754 fd = open(realname, O_WRONLY | O_CREAT | ((append == APPEND) ?
1755 O_APPEND : (tmp ? O_EXCL : O_TRUNC)), S_IRUSR |
1756 S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
1758 /* Set the umask back to the user's original value. */
1759 umask(original_umask);
1761 /* If we couldn't open the file, give up. */
1762 if (fd == -1) {
1763 statusbar(_("Error writing %s: %s"), realname,
1764 strerror(errno));
1766 /* tempname has been set only if we're prepending. */
1767 if (tempname != NULL)
1768 unlink(tempname);
1769 goto cleanup_and_exit;
1772 f = fdopen(fd, (append == APPEND) ? "ab" : "wb");
1774 if (f == NULL) {
1775 statusbar(_("Error writing %s: %s"), realname,
1776 strerror(errno));
1777 close(fd);
1778 goto cleanup_and_exit;
1782 /* There might not be a magicline. There won't be when writing out
1783 * a selection. */
1784 assert(openfile->fileage != NULL && openfile->filebot != NULL);
1786 while (fileptr != NULL) {
1787 size_t data_len = strlen(fileptr->data), size;
1789 /* Convert newlines to nulls, just before we write to disk. */
1790 sunder(fileptr->data);
1792 size = fwrite(fileptr->data, sizeof(char), data_len, f);
1794 /* Convert nulls to newlines. data_len is the string's real
1795 * length. */
1796 unsunder(fileptr->data, data_len);
1798 if (size < data_len) {
1799 statusbar(_("Error writing %s: %s"), realname,
1800 strerror(errno));
1801 fclose(f);
1802 goto cleanup_and_exit;
1805 /* If we're on the last line of the file, don't write a newline
1806 * character after it. If the last line of the file is blank,
1807 * this means that zero bytes are written, in which case we
1808 * don't count the last line in the total lines written. */
1809 if (fileptr == openfile->filebot) {
1810 if (fileptr->data[0] == '\0')
1811 lineswritten--;
1812 } else {
1813 #ifndef NANO_TINY
1814 if (openfile->fmt == DOS_FILE || openfile->fmt ==
1815 MAC_FILE) {
1816 if (putc('\r', f) == EOF) {
1817 statusbar(_("Error writing %s: %s"), realname,
1818 strerror(errno));
1819 fclose(f);
1820 goto cleanup_and_exit;
1824 if (openfile->fmt != MAC_FILE) {
1825 #endif
1826 if (putc('\n', f) == EOF) {
1827 statusbar(_("Error writing %s: %s"), realname,
1828 strerror(errno));
1829 fclose(f);
1830 goto cleanup_and_exit;
1832 #ifndef NANO_TINY
1834 #endif
1837 fileptr = fileptr->next;
1838 lineswritten++;
1841 /* If we're prepending, open the temp file, and append it to f. */
1842 if (append == PREPEND) {
1843 int fd_source;
1844 FILE *f_source = NULL;
1846 fd_source = open(tempname, O_RDONLY | O_CREAT, S_IRUSR | S_IWUSR);
1848 if (fd_source != -1) {
1849 f_source = fdopen(fd_source, "rb");
1850 if (f_source == NULL)
1851 close(fd_source);
1854 if (f_source == NULL) {
1855 statusbar(_("Error reading %s: %s"), tempname,
1856 strerror(errno));
1857 beep();
1858 fclose(f);
1859 goto cleanup_and_exit;
1862 if (copy_file(f_source, f) == -1 || unlink(tempname) == -1) {
1863 statusbar(_("Error writing %s: %s"), realname,
1864 strerror(errno));
1865 goto cleanup_and_exit;
1867 } else if (fclose(f) != 0) {
1868 statusbar(_("Error writing %s: %s"), realname,
1869 strerror(errno));
1870 goto cleanup_and_exit;
1873 if (!tmp && append == OVERWRITE) {
1874 if (!nonamechange) {
1875 openfile->filename = mallocstrcpy(openfile->filename,
1876 realname);
1877 #ifdef ENABLE_COLOR
1878 /* We might have changed the filename, so update the colors
1879 * to account for it, and then make sure we're using
1880 * them. */
1881 color_update();
1882 color_init();
1884 /* If color syntaxes are available and turned on, we need to
1885 * call edit_refresh(). */
1886 if (openfile->colorstrings != NULL &&
1887 !ISSET(NO_COLOR_SYNTAX))
1888 edit_refresh();
1889 #endif
1892 #ifndef NANO_TINY
1893 /* Update current_stat to reference the file as it is now. */
1894 if (openfile->current_stat == NULL)
1895 openfile->current_stat =
1896 (struct stat *)nmalloc(sizeof(struct stat));
1897 stat(realname, openfile->current_stat);
1898 #endif
1900 statusbar(P_("Wrote %lu line", "Wrote %lu lines",
1901 (unsigned long)lineswritten),
1902 (unsigned long)lineswritten);
1903 openfile->modified = FALSE;
1904 titlebar(NULL);
1907 retval = TRUE;
1909 cleanup_and_exit:
1910 free(realname);
1911 if (tempname != NULL)
1912 free(tempname);
1914 return retval;
1917 #ifndef NANO_TINY
1918 /* Write a marked selection from a file out to disk. Return TRUE on
1919 * success or FALSE on error. */
1920 bool write_marked_file(const char *name, FILE *f_open, bool tmp,
1921 append_type append)
1923 bool retval;
1924 bool old_modified = openfile->modified;
1925 /* write_file() unsets the modified flag. */
1926 bool added_magicline = FALSE;
1927 /* Whether we added a magicline after filebot. */
1928 filestruct *top, *bot;
1929 size_t top_x, bot_x;
1931 assert(openfile->mark_set);
1933 /* Partition the filestruct so that it contains only the marked
1934 * text. */
1935 mark_order((const filestruct **)&top, &top_x,
1936 (const filestruct **)&bot, &bot_x, NULL);
1937 filepart = partition_filestruct(top, top_x, bot, bot_x);
1939 /* Handle the magicline if the NO_NEWLINES flag isn't set. If the
1940 * line at filebot is blank, treat it as the magicline and hence the
1941 * end of the file. Otherwise, add a magicline and treat it as the
1942 * end of the file. */
1943 if (!ISSET(NO_NEWLINES) &&
1944 (added_magicline = (openfile->filebot->data[0] != '\0')))
1945 new_magicline();
1947 retval = write_file(name, f_open, tmp, append, TRUE);
1949 /* If the NO_NEWLINES flag isn't set, and we added a magicline,
1950 * remove it now. */
1951 if (!ISSET(NO_NEWLINES) && added_magicline)
1952 remove_magicline();
1954 /* Unpartition the filestruct so that it contains all the text
1955 * again. */
1956 unpartition_filestruct(&filepart);
1958 if (old_modified)
1959 set_modified();
1961 return retval;
1963 #endif /* !NANO_TINY */
1965 /* Write the current file to disk. If the mark is on, write the current
1966 * marked selection to disk. If exiting is TRUE, write the file to disk
1967 * regardless of whether the mark is on, and without prompting if the
1968 * TEMP_FILE flag is set. Return TRUE on success or FALSE on error. */
1969 bool do_writeout(bool exiting)
1971 int i;
1972 append_type append = OVERWRITE;
1973 char *ans;
1974 /* The last answer the user typed at the statusbar prompt. */
1975 #ifdef NANO_EXTRA
1976 static bool did_credits = FALSE;
1977 #endif
1978 bool retval = FALSE, meta_key = FALSE, func_key = FALSE;
1979 const sc *s;
1981 currmenu = MWRITEFILE;
1983 if (exiting && openfile->filename[0] != '\0' && ISSET(TEMP_FILE)) {
1984 retval = write_file(openfile->filename, NULL, FALSE, OVERWRITE,
1985 FALSE);
1987 /* Write succeeded. */
1988 if (retval)
1989 return retval;
1992 ans = mallocstrcpy(NULL,
1993 #ifndef NANO_TINY
1994 (!exiting && openfile->mark_set) ? "" :
1995 #endif
1996 openfile->filename);
1998 while (TRUE) {
1999 const char *msg;
2000 #ifndef NANO_TINY
2001 const char *formatstr, *backupstr;
2003 formatstr = (openfile->fmt == DOS_FILE) ?
2004 _(" [DOS Format]") : (openfile->fmt == MAC_FILE) ?
2005 _(" [Mac Format]") : "";
2007 backupstr = ISSET(BACKUP_FILE) ? _(" [Backup]") : "";
2009 /* If we're using restricted mode, don't display the "Write
2010 * Selection to File" prompt. This function is disabled, since
2011 * it allows reading from or writing to files not specified on
2012 * the command line. */
2013 if (!ISSET(RESTRICTED) && !exiting && openfile->mark_set)
2014 msg = (append == PREPEND) ?
2015 _("Prepend Selection to File") : (append == APPEND) ?
2016 _("Append Selection to File") :
2017 _("Write Selection to File");
2018 else
2019 #endif /* !NANO_TINY */
2020 msg = (append == PREPEND) ? _("File Name to Prepend to") :
2021 (append == APPEND) ? _("File Name to Append to") :
2022 _("File Name to Write");
2024 /* If we're using restricted mode, the filename isn't blank,
2025 * and we're at the "Write File" prompt, disable tab
2026 * completion. */
2027 i = do_prompt(!ISSET(RESTRICTED) ||
2028 openfile->filename[0] == '\0',
2029 #ifndef DISABLE_TABCOMP
2030 TRUE,
2031 #endif
2032 MWRITEFILE, ans,
2033 &meta_key, &func_key,
2034 #ifndef NANO_TINY
2035 NULL,
2036 #endif
2037 edit_refresh, "%s%s%s", msg,
2038 #ifndef NANO_TINY
2039 formatstr, backupstr
2040 #else
2041 "", ""
2042 #endif
2045 /* If the filename or command begins with a newline (i.e. an
2046 * encoded null), treat it as though it's blank. */
2047 if (i < 0 || *answer == '\n') {
2048 statusbar(_("Cancelled"));
2049 retval = FALSE;
2050 break;
2051 } else {
2052 ans = mallocstrcpy(ans, answer);
2053 s = get_shortcut(currmenu, &i, &meta_key, &func_key);
2055 #ifndef DISABLE_BROWSER
2056 if (s && s->scfunc == TO_FILES_MSG) {
2057 char *tmp = do_browse_from(answer);
2059 if (tmp == NULL)
2060 continue;
2062 /* We have a file now. Indicate this. */
2063 free(answer);
2064 answer = tmp;
2065 } else
2066 #endif /* !DISABLE_BROWSER */
2067 #ifndef NANO_TINY
2068 if (s && s->scfunc == DOS_FORMAT_MSG) {
2069 openfile->fmt = (openfile->fmt == DOS_FILE) ? NIX_FILE :
2070 DOS_FILE;
2071 continue;
2072 } else if (s && s->scfunc == MAC_FORMAT_MSG) {
2073 openfile->fmt = (openfile->fmt == MAC_FILE) ? NIX_FILE :
2074 MAC_FILE;
2075 continue;
2076 } else if (s && s->scfunc == BACKUP_FILE_MSG) {
2077 TOGGLE(BACKUP_FILE);
2078 continue;
2079 } else
2080 #endif /* !NANO_TINY */
2081 if (s && s->scfunc == PREPEND_MSG) {
2082 append = (append == PREPEND) ? OVERWRITE : PREPEND;
2083 continue;
2084 } else if (s && s->scfunc == APPEND_MSG) {
2085 append = (append == APPEND) ? OVERWRITE : APPEND;
2086 continue;
2087 } else if (s && s->scfunc == DO_HELP_VOID) {
2088 continue;
2091 #ifdef DEBUG
2092 fprintf(stderr, "filename is %s\n", answer);
2093 #endif
2095 #ifdef NANO_EXTRA
2096 /* If the current file has been modified, we've pressed
2097 * Ctrl-X at the edit window to exit, we've pressed "y" at
2098 * the "Save modified buffer" prompt to save, we've entered
2099 * "zzy" as the filename to save under (hence "xyzzy"), and
2100 * this is the first time we've done this, show an Easter
2101 * egg. Display the credits. */
2102 if (!did_credits && exiting && !ISSET(TEMP_FILE) &&
2103 strcasecmp(answer, "zzy") == 0) {
2104 do_credits();
2105 did_credits = TRUE;
2106 retval = FALSE;
2107 break;
2109 #endif
2111 if (append == OVERWRITE) {
2112 size_t answer_len = strlen(answer);
2113 bool name_exists, do_warning;
2114 char *full_answer, *full_filename;
2115 struct stat st;
2117 /* Convert newlines to nulls, just before we get the
2118 * full path. */
2119 sunder(answer);
2121 full_answer = get_full_path(answer);
2122 full_filename = get_full_path(openfile->filename);
2123 name_exists = (stat((full_answer == NULL) ? answer :
2124 full_answer, &st) != -1);
2125 if (openfile->filename[0] == '\0')
2126 do_warning = name_exists;
2127 else
2128 do_warning = (strcmp((full_answer == NULL) ?
2129 answer : full_answer, (full_filename == NULL) ?
2130 openfile->filename : full_filename) != 0);
2132 /* Convert nulls to newlines. answer_len is the
2133 * string's real length. */
2134 unsunder(answer, answer_len);
2136 if (full_filename != NULL)
2137 free(full_filename);
2138 if (full_answer != NULL)
2139 free(full_answer);
2141 if (do_warning) {
2142 /* If we're using restricted mode, we aren't allowed
2143 * to overwrite an existing file with the current
2144 * file. We also aren't allowed to change the name
2145 * of the current file if it has one, because that
2146 * would allow reading from or writing to files not
2147 * specified on the command line. */
2148 if (ISSET(RESTRICTED))
2149 continue;
2151 if (name_exists) {
2152 i = do_yesno_prompt(FALSE,
2153 _("File exists, OVERWRITE ? "));
2154 if (i == 0 || i == -1)
2155 continue;
2156 } else
2157 #ifndef NANO_TINY
2158 if (exiting || !openfile->mark_set)
2159 #endif
2161 i = do_yesno_prompt(FALSE,
2162 _("Save file under DIFFERENT NAME ? "));
2163 if (i == 0 || i == -1)
2164 continue;
2167 #ifndef NANO_TINY
2168 /* Complain if the file exists, the name hasn't changed, and the
2169 stat information we had before does not match what we have now */
2170 else if (name_exists && openfile->current_stat && (openfile->current_stat->st_mtime < st.st_mtime ||
2171 openfile->current_stat->st_dev != st.st_dev || openfile->current_stat->st_ino != st.st_ino)) {
2172 i = do_yesno_prompt(FALSE,
2173 _("File was modified since you opened it, continue saving ? "));
2174 if (i == 0 || i == -1)
2175 continue;
2177 #endif
2181 /* Convert newlines to nulls, just before we save the
2182 * file. */
2183 sunder(answer);
2184 align(&answer);
2186 /* Here's where we allow the selected text to be written to
2187 * a separate file. If we're using restricted mode, this
2188 * function is disabled, since it allows reading from or
2189 * writing to files not specified on the command line. */
2190 retval =
2191 #ifndef NANO_TINY
2192 (!ISSET(RESTRICTED) && !exiting && openfile->mark_set) ?
2193 write_marked_file(answer, NULL, FALSE, append) :
2194 #endif
2195 write_file(answer, NULL, FALSE, append, FALSE);
2197 break;
2201 free(ans);
2203 return retval;
2206 /* Write the current file to disk. If the mark is on, write the current
2207 * marked selection to disk. */
2208 void do_writeout_void(void)
2210 do_writeout(FALSE);
2211 display_main_list();
2214 /* Return a malloc()ed string containing the actual directory, used to
2215 * convert ~user/ and ~/ notation. */
2216 char *real_dir_from_tilde(const char *buf)
2218 char *retval;
2220 assert(buf != NULL);
2222 if (*buf == '~') {
2223 size_t i = 1;
2224 char *tilde_dir;
2226 /* Figure out how much of the string we need to compare. */
2227 for (; buf[i] != '/' && buf[i] != '\0'; i++)
2230 /* Get the home directory. */
2231 if (i == 1) {
2232 get_homedir();
2233 tilde_dir = mallocstrcpy(NULL, homedir);
2234 } else {
2235 const struct passwd *userdata;
2237 tilde_dir = mallocstrncpy(NULL, buf, i + 1);
2238 tilde_dir[i] = '\0';
2240 do {
2241 userdata = getpwent();
2242 } while (userdata != NULL && strcmp(userdata->pw_name,
2243 tilde_dir + 1) != 0);
2244 endpwent();
2245 if (userdata != NULL)
2246 tilde_dir = mallocstrcpy(tilde_dir, userdata->pw_dir);
2249 retval = charalloc(strlen(tilde_dir) + strlen(buf + i) + 1);
2250 sprintf(retval, "%s%s", tilde_dir, buf + i);
2252 free(tilde_dir);
2253 } else
2254 retval = mallocstrcpy(NULL, buf);
2256 return retval;
2259 #if !defined(DISABLE_TABCOMP) || !defined(DISABLE_BROWSER)
2260 /* Our sort routine for file listings. Sort alphabetically and
2261 * case-insensitively, and sort directories before filenames. */
2262 int diralphasort(const void *va, const void *vb)
2264 struct stat fileinfo;
2265 const char *a = *(const char *const *)va;
2266 const char *b = *(const char *const *)vb;
2267 bool aisdir = stat(a, &fileinfo) != -1 && S_ISDIR(fileinfo.st_mode);
2268 bool bisdir = stat(b, &fileinfo) != -1 && S_ISDIR(fileinfo.st_mode);
2270 if (aisdir && !bisdir)
2271 return -1;
2272 if (!aisdir && bisdir)
2273 return 1;
2275 /* Standard function brain damage: We should be sorting
2276 * alphabetically and case-insensitively according to the current
2277 * locale, but there's no standard strcasecoll() function, so we
2278 * have to use multibyte strcasecmp() instead, */
2279 return mbstrcasecmp(a, b);
2282 /* Free the memory allocated for array, which should contain len
2283 * elements. */
2284 void free_chararray(char **array, size_t len)
2286 assert(array != NULL);
2288 for (; len > 0; len--)
2289 free(array[len - 1]);
2290 free(array);
2292 #endif
2294 #ifndef DISABLE_TABCOMP
2295 /* Is the given path a directory? */
2296 bool is_dir(const char *buf)
2298 char *dirptr;
2299 struct stat fileinfo;
2300 bool retval;
2302 assert(buf != NULL);
2304 dirptr = real_dir_from_tilde(buf);
2306 retval = (stat(dirptr, &fileinfo) != -1 &&
2307 S_ISDIR(fileinfo.st_mode));
2309 free(dirptr);
2311 return retval;
2314 /* These functions, username_tab_completion(), cwd_tab_completion()
2315 * (originally exe_n_cwd_tab_completion()), and input_tab(), were
2316 * adapted from busybox 0.46 (cmdedit.c). Here is the notice from that
2317 * file, with the copyright years updated:
2319 * Termios command line History and Editting, originally
2320 * intended for NetBSD sh (ash)
2321 * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
2322 * Main code: Adam Rogoyski <rogoyski@cs.utexas.edu>
2323 * Etc: Dave Cinege <dcinege@psychosis.com>
2324 * Majorly adjusted/re-written for busybox:
2325 * Erik Andersen <andersee@debian.org>
2327 * You may use this code as you wish, so long as the original author(s)
2328 * are attributed in any redistributions of the source code.
2329 * This code is 'as is' with no warranty.
2330 * This code may safely be consumed by a BSD or GPL license. */
2332 /* We consider the first buf_len characters of buf for ~username tab
2333 * completion. */
2334 char **username_tab_completion(const char *buf, size_t *num_matches,
2335 size_t buf_len)
2337 char **matches = NULL;
2338 const struct passwd *userdata;
2340 assert(buf != NULL && num_matches != NULL && buf_len > 0);
2342 *num_matches = 0;
2344 while ((userdata = getpwent()) != NULL) {
2345 if (strncmp(userdata->pw_name, buf + 1, buf_len - 1) == 0) {
2346 /* Cool, found a match. Add it to the list. This makes a
2347 * lot more sense to me (Chris) this way... */
2349 #ifndef DISABLE_OPERATINGDIR
2350 /* ...unless the match exists outside the operating
2351 * directory, in which case just go to the next match. */
2352 if (check_operating_dir(userdata->pw_dir, TRUE))
2353 continue;
2354 #endif
2356 matches = (char **)nrealloc(matches, (*num_matches + 1) *
2357 sizeof(char *));
2358 matches[*num_matches] =
2359 charalloc(strlen(userdata->pw_name) + 2);
2360 sprintf(matches[*num_matches], "~%s", userdata->pw_name);
2361 ++(*num_matches);
2364 endpwent();
2366 return matches;
2369 /* We consider the first buf_len characters of buf for filename tab
2370 * completion. */
2371 char **cwd_tab_completion(const char *buf, bool allow_files, size_t
2372 *num_matches, size_t buf_len)
2374 char *dirname = mallocstrcpy(NULL, buf), *filename;
2375 #ifndef DISABLE_OPERATINGDIR
2376 size_t dirnamelen;
2377 #endif
2378 size_t filenamelen;
2379 char **matches = NULL;
2380 DIR *dir;
2381 const struct dirent *nextdir;
2383 assert(dirname != NULL && num_matches != NULL);
2385 *num_matches = 0;
2386 null_at(&dirname, buf_len);
2388 /* Okie, if there's a / in the buffer, strip out the directory
2389 * part. */
2390 filename = strrchr(dirname, '/');
2391 if (filename != NULL) {
2392 char *tmpdirname = filename + 1;
2394 filename = mallocstrcpy(NULL, tmpdirname);
2395 *tmpdirname = '\0';
2396 tmpdirname = dirname;
2397 dirname = real_dir_from_tilde(dirname);
2398 free(tmpdirname);
2399 } else {
2400 filename = dirname;
2401 dirname = mallocstrcpy(NULL, "./");
2404 assert(dirname[strlen(dirname) - 1] == '/');
2406 dir = opendir(dirname);
2408 if (dir == NULL) {
2409 /* Don't print an error, just shut up and return. */
2410 beep();
2411 free(filename);
2412 free(dirname);
2413 return NULL;
2416 #ifndef DISABLE_OPERATINGDIR
2417 dirnamelen = strlen(dirname);
2418 #endif
2419 filenamelen = strlen(filename);
2421 while ((nextdir = readdir(dir)) != NULL) {
2422 bool skip_match = FALSE;
2424 #ifdef DEBUG
2425 fprintf(stderr, "Comparing \'%s\'\n", nextdir->d_name);
2426 #endif
2427 /* See if this matches. */
2428 if (strncmp(nextdir->d_name, filename, filenamelen) == 0 &&
2429 (*filename == '.' || (strcmp(nextdir->d_name, ".") !=
2430 0 && strcmp(nextdir->d_name, "..") != 0))) {
2431 /* Cool, found a match. Add it to the list. This makes a
2432 * lot more sense to me (Chris) this way... */
2434 char *tmp = charalloc(strlen(dirname) +
2435 strlen(nextdir->d_name) + 1);
2436 sprintf(tmp, "%s%s", dirname, nextdir->d_name);
2438 #ifndef DISABLE_OPERATINGDIR
2439 /* ...unless the match exists outside the operating
2440 * directory, in which case just go to the next match. */
2441 if (check_operating_dir(tmp, TRUE))
2442 skip_match = TRUE;
2443 #endif
2445 /* ...or unless the match isn't a directory and allow_files
2446 * isn't set, in which case just go to the next match. */
2447 if (!allow_files && !is_dir(tmp))
2448 skip_match = TRUE;
2450 free(tmp);
2452 if (skip_match)
2453 continue;
2455 matches = (char **)nrealloc(matches, (*num_matches + 1) *
2456 sizeof(char *));
2457 matches[*num_matches] = mallocstrcpy(NULL, nextdir->d_name);
2458 ++(*num_matches);
2462 closedir(dir);
2463 free(dirname);
2464 free(filename);
2466 return matches;
2469 /* Do tab completion. place refers to how much the statusbar cursor
2470 * position should be advanced. refresh_func is the function we will
2471 * call to refresh the edit window. */
2472 char *input_tab(char *buf, bool allow_files, size_t *place, bool
2473 *lastwastab, void (*refresh_func)(void), bool *list)
2475 size_t num_matches = 0, buf_len;
2476 char **matches = NULL;
2478 assert(buf != NULL && place != NULL && *place <= strlen(buf) && lastwastab != NULL && refresh_func != NULL && list != NULL);
2480 *list = FALSE;
2482 /* If the word starts with `~' and there is no slash in the word,
2483 * then try completing this word as a username. */
2484 if (*place > 0 && *buf == '~') {
2485 const char *bob = strchr(buf, '/');
2487 if (bob == NULL || bob >= buf + *place)
2488 matches = username_tab_completion(buf, &num_matches,
2489 *place);
2492 /* Match against files relative to the current working directory. */
2493 if (matches == NULL)
2494 matches = cwd_tab_completion(buf, allow_files, &num_matches,
2495 *place);
2497 buf_len = strlen(buf);
2499 if (num_matches == 0 || *place != buf_len)
2500 beep();
2501 else {
2502 size_t match, common_len = 0;
2503 char *mzero;
2504 const char *lastslash = revstrstr(buf, "/", buf + *place);
2505 size_t lastslash_len = (lastslash == NULL) ? 0 :
2506 lastslash - buf + 1;
2507 char *match1_mb = charalloc(mb_cur_max() + 1);
2508 char *match2_mb = charalloc(mb_cur_max() + 1);
2509 int match1_mb_len, match2_mb_len;
2511 while (TRUE) {
2512 for (match = 1; match < num_matches; match++) {
2513 /* Get the number of single-byte characters that all the
2514 * matches have in common. */
2515 match1_mb_len = parse_mbchar(matches[0] + common_len,
2516 match1_mb, NULL);
2517 match2_mb_len = parse_mbchar(matches[match] +
2518 common_len, match2_mb, NULL);
2519 match1_mb[match1_mb_len] = '\0';
2520 match2_mb[match2_mb_len] = '\0';
2521 if (strcmp(match1_mb, match2_mb) != 0)
2522 break;
2525 if (match < num_matches || matches[0][common_len] == '\0')
2526 break;
2528 common_len += parse_mbchar(buf + common_len, NULL, NULL);
2531 free(match1_mb);
2532 free(match2_mb);
2534 mzero = charalloc(lastslash_len + common_len + 1);
2536 strncpy(mzero, buf, lastslash_len);
2537 strncpy(mzero + lastslash_len, matches[0], common_len);
2539 common_len += lastslash_len;
2540 mzero[common_len] = '\0';
2542 assert(common_len >= *place);
2544 if (num_matches == 1 && is_dir(mzero)) {
2545 mzero[common_len++] = '/';
2547 assert(common_len > *place);
2550 if (num_matches > 1 && (common_len != *place || !*lastwastab))
2551 beep();
2553 /* If there is more of a match to display on the statusbar, show
2554 * it. We reset lastwastab to FALSE: it requires pressing Tab
2555 * twice in succession with no statusbar changes to see a match
2556 * list. */
2557 if (common_len != *place) {
2558 *lastwastab = FALSE;
2559 buf = charealloc(buf, common_len + buf_len - *place + 1);
2560 charmove(buf + common_len, buf + *place, buf_len -
2561 *place + 1);
2562 strncpy(buf, mzero, common_len);
2563 *place = common_len;
2564 } else if (!*lastwastab || num_matches < 2)
2565 *lastwastab = TRUE;
2566 else {
2567 int longest_name = 0, ncols, editline = 0;
2569 /* Now we show a list of the available choices. */
2570 assert(num_matches > 1);
2572 /* Sort the list. */
2573 qsort(matches, num_matches, sizeof(char *), diralphasort);
2575 for (match = 0; match < num_matches; match++) {
2576 common_len = strnlenpt(matches[match], COLS - 1);
2578 if (common_len > COLS - 1) {
2579 longest_name = COLS - 1;
2580 break;
2583 if (common_len > longest_name)
2584 longest_name = common_len;
2587 assert(longest_name <= COLS - 1);
2589 /* Each column will be (longest_name + 2) columns wide, i.e.
2590 * two spaces between columns, except that there will be
2591 * only one space after the last column. */
2592 ncols = (COLS + 1) / (longest_name + 2);
2594 /* Blank the edit window, and print the matches out
2595 * there. */
2596 blank_edit();
2597 wmove(edit, 0, 0);
2599 /* Disable el cursor. */
2600 curs_set(0);
2602 for (match = 0; match < num_matches; match++) {
2603 char *disp;
2605 wmove(edit, editline, (longest_name + 2) *
2606 (match % ncols));
2608 if (match % ncols == 0 &&
2609 editline == editwinrows - 1 &&
2610 num_matches - match > ncols) {
2611 waddstr(edit, _("(more)"));
2612 break;
2615 disp = display_string(matches[match], 0, longest_name,
2616 FALSE);
2617 waddstr(edit, disp);
2618 free(disp);
2620 if ((match + 1) % ncols == 0)
2621 editline++;
2624 wnoutrefresh(edit);
2625 *list = TRUE;
2628 free(mzero);
2631 free_chararray(matches, num_matches);
2633 /* Only refresh the edit window if we don't have a list of filename
2634 * matches on it. */
2635 if (!*list)
2636 refresh_func();
2638 /* Enable el cursor. */
2639 curs_set(1);
2641 return buf;
2643 #endif /* !DISABLE_TABCOMP */
2645 /* Only print the last part of a path. Isn't there a shell command for
2646 * this? */
2647 const char *tail(const char *foo)
2649 const char *tmp = strrchr(foo, '/');
2651 if (tmp == NULL)
2652 tmp = foo;
2653 else
2654 tmp++;
2656 return tmp;
2659 #if !defined(NANO_TINY) && defined(ENABLE_NANORC)
2660 /* Return $HOME/.nano_history, or NULL if we can't find the home
2661 * directory. The string is dynamically allocated, and should be
2662 * freed. */
2663 char *histfilename(void)
2665 char *nanohist = NULL;
2667 if (homedir != NULL) {
2668 size_t homelen = strlen(homedir);
2670 nanohist = charalloc(homelen + 15);
2671 strcpy(nanohist, homedir);
2672 strcpy(nanohist + homelen, "/.nano_history");
2675 return nanohist;
2678 /* Load histories from ~/.nano_history. */
2679 void load_history(void)
2681 char *nanohist = histfilename();
2683 /* Assume do_rcfile() has reported a missing home directory. */
2684 if (nanohist != NULL) {
2685 FILE *hist = fopen(nanohist, "rb");
2687 if (hist == NULL) {
2688 if (errno != ENOENT) {
2689 /* Don't save history when we quit. */
2690 UNSET(HISTORYLOG);
2691 rcfile_error(N_("Error reading %s: %s"), nanohist,
2692 strerror(errno));
2693 fprintf(stderr,
2694 _("\nPress Enter to continue starting nano.\n"));
2695 while (getchar() != '\n')
2698 } else {
2699 /* Load a history list (first the search history, then the
2700 * replace history) from the oldest entry to the newest.
2701 * Assume the last history entry is a blank line. */
2702 filestruct **history = &search_history;
2703 char *line = NULL;
2704 size_t buf_len = 0;
2705 ssize_t read;
2707 while ((read = getline(&line, &buf_len, hist)) >= 0) {
2708 if (read > 0 && line[read - 1] == '\n') {
2709 read--;
2710 line[read] = '\0';
2712 if (read > 0) {
2713 unsunder(line, read);
2714 update_history(history, line);
2715 } else
2716 history = &replace_history;
2719 fclose(hist);
2720 free(line);
2722 free(nanohist);
2726 /* Write the lines of a history list, starting with the line at h, to
2727 * the open file at hist. Return TRUE if the write succeeded, and FALSE
2728 * otherwise. */
2729 bool writehist(FILE *hist, filestruct *h)
2731 filestruct *p;
2733 /* Write a history list from the oldest entry to the newest. Assume
2734 * the last history entry is a blank line. */
2735 for (p = h; p != NULL; p = p->next) {
2736 size_t p_len = strlen(p->data);
2738 sunder(p->data);
2740 if (fwrite(p->data, sizeof(char), p_len, hist) < p_len ||
2741 putc('\n', hist) == EOF)
2742 return FALSE;
2745 return TRUE;
2748 /* Save histories to ~/.nano_history. */
2749 void save_history(void)
2751 char *nanohist;
2753 /* Don't save unchanged or empty histories. */
2754 if (!history_has_changed() || (searchbot->lineno == 1 &&
2755 replacebot->lineno == 1))
2756 return;
2758 nanohist = histfilename();
2760 if (nanohist != NULL) {
2761 FILE *hist = fopen(nanohist, "wb");
2763 if (hist == NULL)
2764 rcfile_error(N_("Error writing %s: %s"), nanohist,
2765 strerror(errno));
2766 else {
2767 /* Make sure no one else can read from or write to the
2768 * history file. */
2769 chmod(nanohist, S_IRUSR | S_IWUSR);
2771 if (!writehist(hist, searchage) || !writehist(hist,
2772 replaceage))
2773 rcfile_error(N_("Error writing %s: %s"), nanohist,
2774 strerror(errno));
2776 fclose(hist);
2779 free(nanohist);
2782 #endif /* !NANO_TINY && ENABLE_NANORC */