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