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