blob: a01ed0af09a99f8cbc783b22fa0feed33cf6ab2c [file] [log] [blame]
Chris Allegretta11b00112000-08-06 21:13:45 +00001/* $Id$ */
Chris Allegrettabceb1b22000-06-19 04:22:15 +00002/**************************************************************************
3 * files.c *
4 * *
David Lawrence Ramseyc13b7f02005-01-01 07:28:15 +00005 * Copyright (C) 1999-2005 Chris Allegretta *
Chris Allegrettabceb1b22000-06-19 04:22:15 +00006 * This program is free software; you can redistribute it and/or modify *
7 * it under the terms of the GNU General Public License as published by *
Chris Allegretta3a24f3f2001-10-24 11:33:54 +00008 * the Free Software Foundation; either version 2, or (at your option) *
Chris Allegrettabceb1b22000-06-19 04:22:15 +00009 * any later version. *
10 * *
11 * This program is distributed in the hope that it will be useful, *
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14 * GNU General Public License for more details. *
15 * *
16 * You should have received a copy of the GNU General Public License *
17 * along with this program; if not, write to the Free Software *
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
19 * *
20 **************************************************************************/
21
Jordi Mallach55381aa2004-11-17 23:17:05 +000022#ifdef HAVE_CONFIG_H
23#include <config.h>
24#endif
Chris Allegretta6efda542001-04-28 18:03:52 +000025
Chris Allegrettabceb1b22000-06-19 04:22:15 +000026#include <stdlib.h>
27#include <string.h>
28#include <stdio.h>
29#include <unistd.h>
Chris Allegrettabb88ece2002-03-25 13:40:39 +000030#include <sys/wait.h>
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +000031#include <utime.h>
Chris Allegrettabceb1b22000-06-19 04:22:15 +000032#include <fcntl.h>
33#include <errno.h>
Chris Allegretta04d848e2000-11-05 17:54:41 +000034#include <ctype.h>
35#include <dirent.h>
Chris Allegrettaee733e62001-01-22 22:17:08 +000036#include <pwd.h>
Chris Allegretta7662c862003-01-13 01:35:15 +000037#include <assert.h>
Chris Allegrettabceb1b22000-06-19 04:22:15 +000038#include "proto.h"
39#include "nano.h"
Chris Allegretta4da1fc62000-06-21 03:00:43 +000040
David Lawrence Ramsey3e0c8a62004-11-04 04:08:18 +000041static file_format fmt = NIX_FILE;
42 /* The format of the current file. */
Chris Allegretta858d9d92003-01-30 00:53:32 +000043
Chris Allegrettabceb1b22000-06-19 04:22:15 +000044/* What happens when there is no file to open? aiee! */
45void new_file(void)
46{
Chris Allegrettadffa2072002-07-24 01:02:26 +000047 fileage = make_new_node(NULL);
David Lawrence Ramseyf6e4d332005-01-14 17:17:11 +000048 fileage->data = mallocstrcpy(NULL, "");
Chris Allegrettabceb1b22000-06-19 04:22:15 +000049 filebot = fileage;
50 edittop = fileage;
Chris Allegrettabceb1b22000-06-19 04:22:15 +000051 current = fileage;
Chris Allegrettadffa2072002-07-24 01:02:26 +000052 current_x = 0;
Chris Allegrettabceb1b22000-06-19 04:22:15 +000053 totlines = 1;
Chris Allegretta1b3381b2001-09-28 21:59:01 +000054 totsize = 0;
Chris Allegrettae6421972001-07-18 01:03:36 +000055
Chris Allegretta7c27be42002-05-05 23:03:54 +000056#ifdef ENABLE_COLOR
57 update_color();
David Lawrence Ramsey760a2dc2004-01-14 06:38:00 +000058 if (ISSET(COLOR_SYNTAX))
59 edit_refresh();
Chris Allegretta7c27be42002-05-05 23:03:54 +000060#endif
Chris Allegrettabceb1b22000-06-19 04:22:15 +000061}
62
David Lawrence Ramsey02517e02004-09-05 21:40:31 +000063/* We make a new line of text from buf. buf is length len. If
64 * first_line_ins is TRUE, then we put the new line at the top of the
65 * file. Otherwise, we assume prev is the last line of the file, and
66 * put our line after prev. */
67filestruct *read_line(char *buf, filestruct *prev, bool *first_line_ins,
68 size_t len)
Chris Allegrettabceb1b22000-06-19 04:22:15 +000069{
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +000070 filestruct *fileptr = (filestruct *)nmalloc(sizeof(filestruct));
David Lawrence Ramseyf21cd102002-06-13 00:40:19 +000071
David Lawrence Ramsey02517e02004-09-05 21:40:31 +000072 /* Convert nulls to newlines. len is the string's real length
73 * here. */
David Lawrence Ramseyf21cd102002-06-13 00:40:19 +000074 unsunder(buf, len);
75
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +000076 assert(strlen(buf) == len);
77
78 fileptr->data = mallocstrcpy(NULL, buf);
Chris Allegrettabceb1b22000-06-19 04:22:15 +000079
Chris Allegretta91841892001-09-21 02:37:01 +000080#ifndef NANO_SMALL
David Lawrence Ramsey02517e02004-09-05 21:40:31 +000081 /* If it's a DOS file (CR LF), and file conversion isn't disabled,
82 * strip out the CR part. */
David Lawrence Ramsey23c44502005-01-27 20:49:07 +000083 if (!ISSET(NO_CONVERT) && len > 0 && buf[len - 1] == '\r')
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +000084 fileptr->data[len - 1] = '\0';
Chris Allegretta91841892001-09-21 02:37:01 +000085#endif
86
David Lawrence Ramsey02517e02004-09-05 21:40:31 +000087 if (*first_line_ins || fileage == NULL) {
88 /* Special case: we're inserting with the cursor on the first
89 * line. */
Chris Allegrettabceb1b22000-06-19 04:22:15 +000090 fileptr->prev = NULL;
91 fileptr->next = fileage;
92 fileptr->lineno = 1;
David Lawrence Ramsey02517e02004-09-05 21:40:31 +000093 if (*first_line_ins) {
94 *first_line_ins = FALSE;
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +000095 /* If we're inserting into the first line of the file, then
David Lawrence Ramsey02517e02004-09-05 21:40:31 +000096 * we want to make sure that our edit buffer stays on the
97 * first line and that fileage stays up to date. */
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +000098 edittop = fileptr;
99 } else
100 filebot = fileptr;
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000101 fileage = fileptr;
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +0000102 } else {
103 assert(prev != NULL);
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000104 fileptr->prev = prev;
105 fileptr->next = NULL;
106 fileptr->lineno = prev->lineno + 1;
107 prev->next = fileptr;
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000108 }
109
110 return fileptr;
111}
112
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000113/* Load a file into the edit buffer. This takes data from the file
114 * struct. */
115void load_file(void)
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000116{
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000117 current = fileage;
118
119#ifdef ENABLE_MULTIBUFFER
120 /* Add a new entry to the open_files structure. */
121 add_open_file(FALSE);
122
123 /* Reinitialize the shortcut list. */
124 shortcut_init(FALSE);
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +0000125#endif
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000126}
127
128void read_file(FILE *f, const char *filename)
129{
130 size_t num_lines = 0;
131 /* The number of lines in the file. */
David Lawrence Ramsey96de1b02005-01-27 21:00:10 +0000132 size_t num_chars;
133 /* The number of characters in the file. */
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000134 size_t len = 0;
135 /* The length of the current line of the file. */
136 size_t i = 0;
137 /* The position in the current line of the file. */
138 size_t bufx = 128;
139 /* The size of each chunk of the file that we read. */
140 char input = '\0';
141 /* The current input character. */
142 char *buf;
143 /* The buffer where we store chunks of the file. */
144 filestruct *fileptr = current;
145 /* The current line of the file. */
146 bool first_line_ins = FALSE;
147 /* Whether we're inserting with the cursor on the first line. */
Chris Allegretta0547eb32002-04-22 23:52:34 +0000148 int input_int;
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000149 /* The current value we read from the file, whether an input
150 * character or EOF. */
151#ifndef NANO_SMALL
David Lawrence Ramsey3e0c8a62004-11-04 04:08:18 +0000152 int format = 0;
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000153 /* 0 = *nix, 1 = DOS, 2 = Mac, 3 = both DOS and Mac. */
154#endif
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000155
Chris Allegretta88b09152001-05-17 11:35:43 +0000156 buf = charalloc(bufx);
Chris Allegretta8f6c0692000-07-19 01:16:18 +0000157 buf[0] = '\0';
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000158
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +0000159 if (current != NULL) {
160 if (current == fileage)
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000161 first_line_ins = TRUE;
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +0000162 else
163 fileptr = current->prev;
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000164 }
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +0000165
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000166 /* For the assertion in read_line(), it must be true that if current
167 * is NULL, then so is fileage. */
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +0000168 assert(current != NULL || fileage == NULL);
169
David Lawrence Ramsey7c1f17a2004-10-03 13:47:26 +0000170#ifndef NANO_SMALL
David Lawrence Ramsey3e0c8a62004-11-04 04:08:18 +0000171 /* We don't know which file format we have yet, so assume it's a
172 * *nix file for now. */
173 fmt = NIX_FILE;
David Lawrence Ramsey7c1f17a2004-10-03 13:47:26 +0000174#endif
175
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000176 /* Read the entire file into the file struct. */
Chris Allegretta0547eb32002-04-22 23:52:34 +0000177 while ((input_int = getc(f)) != EOF) {
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000178 input = (char)input_int;
Chris Allegretta2598c662002-03-28 01:59:34 +0000179
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000180 /* If it's a *nix file (LF) or a DOS file (CR LF), and file
181 * conversion isn't disabled, handle it! */
Chris Allegrettaf6cba642002-03-26 13:05:54 +0000182 if (input == '\n') {
David Lawrence Ramseyf21cd102002-06-13 00:40:19 +0000183
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000184#ifndef NANO_SMALL
David Lawrence Ramsey3e0c8a62004-11-04 04:08:18 +0000185 /* If there's a CR before the LF, set format to DOS if we
186 * currently think this is a *nix file, or to both if we
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000187 * currently think it's a Mac file. */
188 if (!ISSET(NO_CONVERT) && i > 0 && buf[i - 1] == '\r' &&
David Lawrence Ramsey3e0c8a62004-11-04 04:08:18 +0000189 (format == 0 || format == 2))
190 format++;
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000191#endif
David Lawrence Ramseyf21cd102002-06-13 00:40:19 +0000192
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000193 /* Read in the line properly. */
194 fileptr = read_line(buf, fileptr, &first_line_ins, len);
195
196 /* Reset the line length in preparation for the next
197 * line. */
David Lawrence Ramseyf21cd102002-06-13 00:40:19 +0000198 len = 0;
199
Adam Rogoyski1e328fb2000-07-08 03:57:16 +0000200 num_lines++;
David Lawrence Ramseyf21cd102002-06-13 00:40:19 +0000201 buf[0] = '\0';
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000202 i = 0;
Chris Allegretta03191762001-09-22 06:38:38 +0000203#ifndef NANO_SMALL
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000204 /* If it's a Mac file (CR without an LF), and file conversion
205 * isn't disabled, handle it! */
David Lawrence Ramseyf21cd102002-06-13 00:40:19 +0000206 } else if (!ISSET(NO_CONVERT) && i > 0 && buf[i - 1] == '\r') {
David Lawrence Ramseyf21cd102002-06-13 00:40:19 +0000207
David Lawrence Ramsey3e0c8a62004-11-04 04:08:18 +0000208 /* If we currently think the file is a *nix file, set format
209 * to Mac. If we currently think the file is a DOS file,
210 * set format to both DOS and Mac. */
211 if (format == 0 || format == 1)
212 format += 2;
David Lawrence Ramseyf21cd102002-06-13 00:40:19 +0000213
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000214 /* Read in the line properly. */
215 fileptr = read_line(buf, fileptr, &first_line_ins, len);
216
217 /* Reset the line length in preparation for the next line.
218 * Since we've already read in the next character, reset it
219 * to 1 instead of 0. */
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +0000220 len = 1;
David Lawrence Ramseyf21cd102002-06-13 00:40:19 +0000221
Chris Allegretta03191762001-09-22 06:38:38 +0000222 num_lines++;
Chris Allegrettaf6cba642002-03-26 13:05:54 +0000223 buf[0] = input;
David Lawrence Ramseyf21cd102002-06-13 00:40:19 +0000224 buf[1] = '\0';
Chris Allegretta03191762001-09-22 06:38:38 +0000225 i = 1;
226#endif
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000227 } else {
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000228 /* Calculate the total length of the line. It might have
229 * nulls in it, so we can't just use strlen() here. */
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +0000230 len++;
231
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000232 /* Now we allocate a bigger buffer 128 characters at a time.
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000233 * If we allocate a lot of space for one line, we may indeed
234 * have to use a buffer this big later on, so we don't
235 * decrease it at all. We do free it at the end, though. */
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000236 if (i >= bufx - 1) {
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000237 bufx += 128;
David Lawrence Ramsey70047ee2003-06-14 20:41:34 +0000238 buf = charealloc(buf, bufx);
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000239 }
David Lawrence Ramsey23c44502005-01-27 20:49:07 +0000240
Chris Allegrettaf6cba642002-03-26 13:05:54 +0000241 buf[i] = input;
David Lawrence Ramseyf21cd102002-06-13 00:40:19 +0000242 buf[i + 1] = '\0';
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000243 i++;
244 }
Chris Allegretta0547eb32002-04-22 23:52:34 +0000245 }
246
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000247 /* This conditional duplicates previous read_byte() behavior.
248 * Perhaps this could use some better handling. */
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +0000249 if (ferror(f))
250 nperror(filename);
251 fclose(f);
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000252
Chris Allegrettac4e3d9e2002-07-21 15:44:13 +0000253#ifndef NANO_SMALL
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000254 /* If file conversion isn't disabled and the last character in this
255 * file is a CR, read it in properly as a Mac format line. */
256 if (len == 0 && !ISSET(NO_CONVERT) && input == '\r') {
David Lawrence Ramsey23c44502005-01-27 20:49:07 +0000257 len = 1;
258
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +0000259 buf[0] = input;
260 buf[1] = '\0';
Chris Allegrettac4e3d9e2002-07-21 15:44:13 +0000261 }
262#endif
Chris Allegretta52c5a6e2002-03-21 05:07:28 +0000263
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000264 /* Did we not get a newline and still have stuff to do? */
265 if (len > 0) {
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +0000266#ifndef NANO_SMALL
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000267 /* If file conversion isn't disabled and the last character in
David Lawrence Ramsey3e0c8a62004-11-04 04:08:18 +0000268 * this file is a CR, set format to Mac if we currently think
269 * the file is a *nix file, or to both DOS and Mac if we
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000270 * currently think the file is a DOS file. */
271 if (!ISSET(NO_CONVERT) && buf[len - 1] == '\r' &&
David Lawrence Ramsey3e0c8a62004-11-04 04:08:18 +0000272 (format == 0 || format == 1))
273 format += 2;
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +0000274#endif
275
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000276 /* Read in the last line properly. */
277 fileptr = read_line(buf, fileptr, &first_line_ins, len);
278 num_lines++;
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000279 }
David Lawrence Ramsey23c44502005-01-27 20:49:07 +0000280
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000281 free(buf);
282
283 /* If we didn't get a file and we don't already have one, make a new
284 * file. */
David Lawrence Ramsey23c44502005-01-27 20:49:07 +0000285 if (fileptr == NULL)
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000286 new_file();
Robert Siemborski63b3d7e2000-07-04 22:15:39 +0000287
Chris Allegrettaf6cba642002-03-26 13:05:54 +0000288 /* Did we try to insert a file of 0 bytes? */
David Lawrence Ramsey2ab03f62002-10-17 02:19:31 +0000289 if (num_lines != 0) {
290 if (current != NULL) {
291 fileptr->next = current;
292 current->prev = fileptr;
293 renumber(current);
294 current_x = 0;
295 placewewant = 0;
296 } else if (fileptr->next == NULL) {
297 filebot = fileptr;
298 new_magicline();
David Lawrence Ramsey2ab03f62002-10-17 02:19:31 +0000299 }
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000300 }
Chris Allegretta76e291b2001-10-14 19:05:10 +0000301
David Lawrence Ramsey23c44502005-01-27 20:49:07 +0000302 get_totals(fileage, filebot, NULL, &num_chars);
303 totsize += num_chars;
304
Chris Allegretta76e291b2001-10-14 19:05:10 +0000305#ifndef NANO_SMALL
David Lawrence Ramsey3e0c8a62004-11-04 04:08:18 +0000306 if (format == 3)
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000307 statusbar(
308 P_("Read %lu line (Converted from DOS and Mac format)",
309 "Read %lu lines (Converted from DOS and Mac format)",
310 (unsigned long)num_lines), (unsigned long)num_lines);
David Lawrence Ramsey3e0c8a62004-11-04 04:08:18 +0000311 else if (format == 2) {
312 fmt = MAC_FILE;
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000313 statusbar(P_("Read %lu line (Converted from Mac format)",
314 "Read %lu lines (Converted from Mac format)",
315 (unsigned long)num_lines), (unsigned long)num_lines);
David Lawrence Ramsey3e0c8a62004-11-04 04:08:18 +0000316 } else if (format == 1) {
317 fmt = DOS_FILE;
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000318 statusbar(P_("Read %lu line (Converted from DOS format)",
319 "Read %lu lines (Converted from DOS format)",
320 (unsigned long)num_lines), (unsigned long)num_lines);
David Lawrence Ramsey7c1f17a2004-10-03 13:47:26 +0000321 } else
Chris Allegretta76e291b2001-10-14 19:05:10 +0000322#endif
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000323 statusbar(P_("Read %lu line", "Read %lu lines",
David Lawrence Ramsey23c44502005-01-27 20:49:07 +0000324 (unsigned long)num_lines), (unsigned long)num_lines);
David Lawrence Ramsey7bf00de2003-09-23 04:25:05 +0000325
Adam Rogoyski1e328fb2000-07-08 03:57:16 +0000326 totlines += num_lines;
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000327}
328
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000329/* Open the file (and decide if it exists). If newfie is TRUE, display
330 * "New File" if the file is missing. Otherwise, say "[filename] not
331 * found".
332 *
333 * Return -2 if we say "New File". Otherwise, -1 if the file isn't
334 * opened, 0 otherwise. The file might still have an error while
335 * reading with a 0 return value. *f is set to the opened file. */
336int open_file(const char *filename, bool newfie, FILE **f)
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000337{
338 int fd;
339 struct stat fileinfo;
340
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000341 assert(f != NULL);
David Lawrence Ramsey23c44502005-01-27 20:49:07 +0000342
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000343 if (filename == NULL || filename[0] == '\0' ||
344 stat(filename, &fileinfo) == -1) {
345 if (newfie) {
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000346 statusbar(_("New File"));
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000347 return -2;
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000348 }
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000349 statusbar(_("\"%s\" not found"), filename);
350 return -1;
Chris Allegretta54c1f792003-01-26 04:11:09 +0000351 } else if (S_ISDIR(fileinfo.st_mode) || S_ISCHR(fileinfo.st_mode) ||
352 S_ISBLK(fileinfo.st_mode)) {
353 /* Don't open character or block files. Sorry, /dev/sndstat! */
David Lawrence Ramseye08765d2004-11-26 20:17:49 +0000354 statusbar(S_ISDIR(fileinfo.st_mode) ? _("\"%s\" is a directory")
355 : _("File \"%s\" is a device file"), filename);
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000356 return -1;
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000357 } else if ((fd = open(filename, O_RDONLY)) == -1) {
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000358 statusbar(_("Error reading %s: %s"), filename, strerror(errno));
359 return -1;
360 } else {
David Lawrence Ramseye08765d2004-11-26 20:17:49 +0000361 /* File is A-OK. Open it in binary mode for our own end-of-line
362 * character munging. */
363 *f = fdopen(fd, "rb");
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000364
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000365 if (*f == NULL) {
David Lawrence Ramseye08765d2004-11-26 20:17:49 +0000366 statusbar(_("Error reading %s: %s"), filename,
367 strerror(errno));
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000368 close(fd);
369 } else
370 statusbar(_("Reading File"));
371 }
372 return 0;
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000373}
374
Chris Allegretta48b06702002-02-22 04:30:50 +0000375/* This function will return the name of the first available extension
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000376 * of a filename (starting with filename.save, then filename.save.1,
377 * etc.). Memory is allocated for the return value. If no writable
David Lawrence Ramsey049e4a52004-08-10 23:05:59 +0000378 * extension exists, we return "". */
Chris Allegretta7162e3d2002-04-06 05:02:14 +0000379char *get_next_filename(const char *name)
Chris Allegretta48b06702002-02-22 04:30:50 +0000380{
381 int i = 0;
David Lawrence Ramsey049e4a52004-08-10 23:05:59 +0000382 char *buf;
383 size_t namelen = strlen(name);
Chris Allegretta48b06702002-02-22 04:30:50 +0000384
David Lawrence Ramsey049e4a52004-08-10 23:05:59 +0000385 buf = charalloc(namelen + num_of_digits(INT_MAX) + 7);
Chris Allegretta48b06702002-02-22 04:30:50 +0000386 strcpy(buf, name);
David Lawrence Ramsey049e4a52004-08-10 23:05:59 +0000387 strcpy(buf + namelen, ".save");
388 namelen += 5;
Chris Allegretta48b06702002-02-22 04:30:50 +0000389
David Lawrence Ramsey6aec4b82004-03-15 20:26:30 +0000390 while (TRUE) {
David Lawrence Ramsey049e4a52004-08-10 23:05:59 +0000391 struct stat fs;
Chris Allegretta48b06702002-02-22 04:30:50 +0000392
393 if (stat(buf, &fs) == -1)
Chris Allegretta7162e3d2002-04-06 05:02:14 +0000394 return buf;
Chris Allegretta48b06702002-02-22 04:30:50 +0000395 if (i == INT_MAX)
396 break;
397
398 i++;
David Lawrence Ramsey049e4a52004-08-10 23:05:59 +0000399 sprintf(buf + namelen, ".%d", i);
Chris Allegretta48b06702002-02-22 04:30:50 +0000400 }
401
Chris Allegretta7162e3d2002-04-06 05:02:14 +0000402 /* We get here only if there is no possible save file. */
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000403 null_at(&buf, 0);
Chris Allegretta48b06702002-02-22 04:30:50 +0000404 return buf;
405}
406
David Lawrence Ramsey146bb602004-08-27 21:02:38 +0000407#ifndef NANO_SMALL
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000408void execute_command(const char *command)
409{
410#ifdef ENABLE_MULTIBUFFER
411 if (ISSET(MULTIBUFFER)) {
412 /* Update the current entry in the open_files structure. */
413 add_open_file(TRUE);
414 new_file();
415 UNSET(MODIFIED);
416 UNSET(MARK_ISSET);
417 }
418#endif /* ENABLE_MULTIBUFFER */
419 open_pipe(command);
420#ifdef ENABLE_MULTIBUFFER
421 /* Add this new entry to the open_files structure. */
422 if (ISSET(MULTIBUFFER))
423 load_file();
424#endif /* ENABLE_MULTIBUFFER */
425}
426#endif /* !NANO_SMALL */
David Lawrence Ramsey146bb602004-08-27 21:02:38 +0000427
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000428/* name is a file name to open. We make a new buffer if necessary, then
429 * open and read the file. */
430void load_buffer(const char *name)
431{
David Lawrence Ramsey3d002ed2004-10-31 22:45:24 +0000432 bool new_buffer = (fileage == NULL
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000433#ifdef ENABLE_MULTIBUFFER
434 || ISSET(MULTIBUFFER)
Chris Allegretta6fe61492001-05-21 12:56:25 +0000435#endif
David Lawrence Ramsey3d002ed2004-10-31 22:45:24 +0000436 );
David Lawrence Ramsey1c293d22004-11-05 16:22:27 +0000437 /* new_buffer says whether we load into this buffer or a new
438 * one. If new_buffer is TRUE, we display "New File" if the
439 * file is not found, and if it is found we set filename and add
440 * a new open_files entry. */
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000441 FILE *f;
442 int rc;
443 /* rc == -2 means that the statusbar displayed "New File". -1
444 * means that the open failed. 0 means success. */
Chris Allegretta6fe61492001-05-21 12:56:25 +0000445
Chris Allegrettaf5de33a2002-02-27 04:14:16 +0000446#ifndef DISABLE_OPERATINGDIR
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000447 if (check_operating_dir(name, FALSE)) {
David Lawrence Ramsey8a2c0ba2004-11-24 20:36:36 +0000448 statusbar(_("Can't insert file from outside of %s"),
449 operating_dir);
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000450 return;
451 }
Chris Allegretta7662c862003-01-13 01:35:15 +0000452#endif
Chris Allegrettaf7c68112002-09-03 22:58:40 +0000453
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000454#ifdef ENABLE_MULTIBUFFER
455 /* Update the current entry in the open_files structure. */
456 add_open_file(TRUE);
457#endif
458
459 rc = open_file(name, new_buffer, &f);
460
461#ifdef ENABLE_MULTIBUFFER
462 if (rc != -1 && ISSET(MULTIBUFFER)) {
463 UNSET(MODIFIED);
464#ifndef NANO_SMALL
465 UNSET(MARK_ISSET);
466#endif
467 }
468#endif
469
470 if (rc != -1 && new_buffer) {
471 filename = mallocstrcpy(filename, name);
472 new_file();
473 }
474
475 if (rc == 0) {
David Lawrence Ramsey78d644a2004-11-05 16:24:35 +0000476 file_format fmt_save = fmt;
477
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000478 read_file(f, filename);
David Lawrence Ramsey78d644a2004-11-05 16:24:35 +0000479
480 /* If we're not loading into a new buffer, preserve the file
481 * format. */
482 if (!new_buffer)
483 fmt = fmt_save;
484
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000485#ifndef NANO_SMALL
486 stat(filename, &originalfilestat);
487#endif
488 }
489
490 /* Add this new entry to the open_files structure if we have
491 * multibuffer support, or to the main filestruct if we don't. */
492 if (rc != -1 && new_buffer)
493 load_file();
494}
495
David Lawrence Ramseybe908f62004-10-01 18:34:30 +0000496void do_insertfile(
497#ifndef NANO_SMALL
498 bool execute
499#else
500 void
501#endif
502 )
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000503{
504 int i;
505 const char *msg;
David Lawrence Ramsey670a56c2004-09-28 16:03:03 +0000506 char *ans = mallocstrcpy(NULL, "");
David Lawrence Ramsey045883a2004-10-05 20:11:31 +0000507 /* The last answer the user typed on the statusbar. */
David Lawrence Ramseyb73c0c02004-11-05 15:25:53 +0000508 filestruct *edittop_save = edittop;
David Lawrence Ramsey036a3d42004-11-15 21:49:21 +0000509 int current_y_save = current_y;
David Lawrence Ramsey4a3879f2004-11-15 20:42:31 +0000510 bool at_edittop = FALSE;
511 /* Whether we're at the top of the edit window. */
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000512
David Lawrence Ramsey487149e2004-10-05 20:24:48 +0000513#ifndef DISABLE_WRAPPING
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000514 wrap_reset();
David Lawrence Ramsey487149e2004-10-05 20:24:48 +0000515#endif
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000516
David Lawrence Ramsey045883a2004-10-05 20:11:31 +0000517 while (TRUE) {
David Lawrence Ramseye5d8f322004-09-30 22:07:21 +0000518#ifndef NANO_SMALL
David Lawrence Ramsey045883a2004-10-05 20:11:31 +0000519 if (execute) {
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000520#ifdef ENABLE_MULTIBUFFER
David Lawrence Ramsey045883a2004-10-05 20:11:31 +0000521 if (ISSET(MULTIBUFFER))
522 msg = N_("Command to execute in new buffer [from %s] ");
523 else
Chris Allegrettaf5de33a2002-02-27 04:14:16 +0000524#endif
David Lawrence Ramsey045883a2004-10-05 20:11:31 +0000525 msg = N_("Command to execute [from %s] ");
526 } else {
David Lawrence Ramseye5d8f322004-09-30 22:07:21 +0000527#endif
528#ifdef ENABLE_MULTIBUFFER
David Lawrence Ramsey045883a2004-10-05 20:11:31 +0000529 if (ISSET(MULTIBUFFER)) {
530 msg = N_("File to insert into new buffer [from %s] ");
531 } else
David Lawrence Ramseye5d8f322004-09-30 22:07:21 +0000532#endif
David Lawrence Ramsey045883a2004-10-05 20:11:31 +0000533 msg = N_("File to insert [from %s] ");
David Lawrence Ramseye5d8f322004-09-30 22:07:21 +0000534#ifndef NANO_SMALL
535 }
536#endif
David Lawrence Ramsey04a8d1c2004-09-28 22:14:58 +0000537
David Lawrence Ramsey045883a2004-10-05 20:11:31 +0000538 i = statusq(TRUE,
David Lawrence Ramseye5d8f322004-09-30 22:07:21 +0000539#ifndef NANO_SMALL
David Lawrence Ramseybe908f62004-10-01 18:34:30 +0000540 execute ? extcmd_list :
David Lawrence Ramseye5d8f322004-09-30 22:07:21 +0000541#endif
542 insertfile_list, ans,
Chris Allegretta7662c862003-01-13 01:35:15 +0000543#ifndef NANO_SMALL
David Lawrence Ramsey6aec4b82004-03-15 20:26:30 +0000544 NULL,
Chris Allegrettaf7c68112002-09-03 22:58:40 +0000545#endif
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000546 _(msg),
547#ifndef DISABLE_OPERATINGDIR
548 operating_dir != NULL && strcmp(operating_dir, ".") != 0 ?
549 operating_dir :
Chris Allegretta7662c862003-01-13 01:35:15 +0000550#endif
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000551 "./");
Chris Allegrettaf5de33a2002-02-27 04:14:16 +0000552
David Lawrence Ramseyfdd3bec2004-11-07 21:30:55 +0000553 /* If we're in multibuffer mode and the filename or command is
554 * blank, open a new buffer instead of canceling. */
David Lawrence Ramseyc136f752004-11-07 18:35:53 +0000555 if (i == -1 || (i == -2
David Lawrence Ramseya084ad92004-11-07 18:22:33 +0000556#ifdef ENABLE_MULTIBUFFER
David Lawrence Ramseyc136f752004-11-07 18:35:53 +0000557 && !ISSET(MULTIBUFFER)
David Lawrence Ramseya084ad92004-11-07 18:22:33 +0000558#endif
David Lawrence Ramseyc136f752004-11-07 18:35:53 +0000559 ))
David Lawrence Ramseya084ad92004-11-07 18:22:33 +0000560 {
David Lawrence Ramsey045883a2004-10-05 20:11:31 +0000561 statusbar(_("Cancelled"));
562 break;
563 } else {
David Lawrence Ramsey31f99362004-11-05 16:01:04 +0000564 size_t pww_save = placewewant;
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000565
David Lawrence Ramsey045883a2004-10-05 20:11:31 +0000566 ans = mallocstrcpy(ans, answer);
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000567
David Lawrence Ramseye5d8f322004-09-30 22:07:21 +0000568#if !defined(NANO_SMALL) && defined(ENABLE_MULTIBUFFER)
David Lawrence Ramsey045883a2004-10-05 20:11:31 +0000569 if (i == TOGGLE_MULTIBUFFER_KEY) {
570 /* Don't allow toggling if we're in view mode. */
571 if (!ISSET(VIEW_MODE))
572 TOGGLE(MULTIBUFFER);
573 continue;
574 }
David Lawrence Ramsey04a8d1c2004-09-28 22:14:58 +0000575#endif
David Lawrence Ramsey6aec4b82004-03-15 20:26:30 +0000576
Chris Allegretta434d6862003-02-03 04:55:17 +0000577#ifndef DISABLE_BROWSER
David Lawrence Ramsey045883a2004-10-05 20:11:31 +0000578 if (i == NANO_TOFILES_KEY) {
579 char *tmp = do_browse_from(answer);
Chris Allegretta434d6862003-02-03 04:55:17 +0000580
David Lawrence Ramsey045883a2004-10-05 20:11:31 +0000581 if (tmp == NULL)
582 continue;
David Lawrence Ramseyb49daec2004-10-05 02:46:24 +0000583 free(answer);
584 answer = tmp;
David Lawrence Ramsey045883a2004-10-05 20:11:31 +0000585
586 /* We have a file now. Get out of the statusbar prompt
587 * cleanly. */
588 statusq_abort();
David Lawrence Ramseyb49daec2004-10-05 02:46:24 +0000589 }
Chris Allegrettaf4b96012001-01-03 07:11:47 +0000590#endif
591
Chris Allegretta52c5a6e2002-03-21 05:07:28 +0000592#ifndef NANO_SMALL
David Lawrence Ramsey045883a2004-10-05 20:11:31 +0000593 if (i == NANO_TOOTHERINSERT_KEY) {
594 execute = !execute;
595 continue;
596 }
David Lawrence Ramseyc15802f2004-11-07 18:09:41 +0000597#endif
David Lawrence Ramseye5d8f322004-09-30 22:07:21 +0000598
David Lawrence Ramseyc15802f2004-11-07 18:09:41 +0000599#ifdef ENABLE_MULTIBUFFER
600 if (!ISSET(MULTIBUFFER)) {
601#endif
602 /* If we're not inserting into a new buffer, partition
603 * the filestruct so that it contains no text and hence
David Lawrence Ramsey4a3879f2004-11-15 20:42:31 +0000604 * looks like a new buffer, and keep track of whether
605 * the top of the partition is the top of the edit
606 * window. */
David Lawrence Ramseyc15802f2004-11-07 18:09:41 +0000607 filepart = partition_filestruct(current, current_x,
608 current, current_x);
David Lawrence Ramsey4a3879f2004-11-15 20:42:31 +0000609 at_edittop = (fileage == edittop);
David Lawrence Ramseyc15802f2004-11-07 18:09:41 +0000610#ifdef ENABLE_MULTIBUFFER
611 }
612#endif
613
614#ifndef NANO_SMALL
David Lawrence Ramsey045883a2004-10-05 20:11:31 +0000615 if (execute)
616 execute_command(answer);
617 else {
David Lawrence Ramseyd717d4b2004-07-10 16:53:17 +0000618#endif
David Lawrence Ramsey045883a2004-10-05 20:11:31 +0000619 answer = mallocstrassn(answer, real_dir_from_tilde(answer));
620 load_buffer(answer);
David Lawrence Ramseyd717d4b2004-07-10 16:53:17 +0000621#ifndef NANO_SMALL
David Lawrence Ramsey045883a2004-10-05 20:11:31 +0000622 }
David Lawrence Ramseyd717d4b2004-07-10 16:53:17 +0000623#endif
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000624
Chris Allegretta355fbe52001-07-14 19:32:47 +0000625#ifdef ENABLE_MULTIBUFFER
David Lawrence Ramseyc15802f2004-11-07 18:09:41 +0000626 if (!ISSET(MULTIBUFFER))
627#endif
628 {
629 filestruct *top_save = fileage;
630
David Lawrence Ramsey036a3d42004-11-15 21:49:21 +0000631 /* If we didn't insert into a new buffer, and we were at
632 * the top of the edit window before, set the saved
633 * value of edittop to the new top of the edit window,
634 * and update the current y-coordinate to account for
635 * the number of lines inserted. */
636 if (at_edittop)
637 edittop_save = fileage;
638 current_y += current_y_save;
639
David Lawrence Ramsey56cf0342004-11-15 18:44:30 +0000640 /* If we didn't insert into a new buffer, unpartition
David Lawrence Ramseyc15802f2004-11-07 18:09:41 +0000641 * the filestruct so that it contains all the text
642 * again. Note that we've replaced the non-text
643 * originally in the partition with the text in the
644 * inserted file/executed command output. */
David Lawrence Ramsey74d87072004-11-22 00:16:23 +0000645 unpartition_filestruct(&filepart);
David Lawrence Ramseyc15802f2004-11-07 18:09:41 +0000646
647 /* Renumber starting with the beginning line of the old
648 * partition. */
649 renumber(top_save);
650
651 /* Set edittop back to what it was before. */
652 edittop = edittop_save;
653 }
654
655#ifdef ENABLE_MULTIBUFFER
David Lawrence Ramsey045883a2004-10-05 20:11:31 +0000656 if (ISSET(MULTIBUFFER)) {
657 /* Update the titlebar. */
658 titlebar(NULL);
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000659
David Lawrence Ramsey045883a2004-10-05 20:11:31 +0000660 /* Reinitialize the shortcut list. */
661 shortcut_init(FALSE);
662 } else {
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000663#endif
David Lawrence Ramsey045883a2004-10-05 20:11:31 +0000664 /* Mark the file as modified. */
665 set_modified();
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000666
David Lawrence Ramseyb73c0c02004-11-05 15:25:53 +0000667 /* Restore the old place we want. */
David Lawrence Ramsey31f99362004-11-05 16:01:04 +0000668 placewewant = pww_save;
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000669#ifdef ENABLE_MULTIBUFFER
David Lawrence Ramsey045883a2004-10-05 20:11:31 +0000670 }
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000671#endif
Chris Allegretta4dc03d52002-05-11 03:04:44 +0000672
David Lawrence Ramsey045883a2004-10-05 20:11:31 +0000673 /* Refresh the screen. */
674 edit_refresh();
675
676 break;
677 }
678 } /* while (TRUE) */
Chris Allegretta7662c862003-01-13 01:35:15 +0000679
David Lawrence Ramsey670a56c2004-09-28 16:03:03 +0000680 free(ans);
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000681}
682
David Lawrence Ramsey72e51ab2004-07-02 14:31:03 +0000683void do_insertfile_void(void)
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000684{
Chris Allegretta355fbe52001-07-14 19:32:47 +0000685#ifdef ENABLE_MULTIBUFFER
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000686 if (ISSET(VIEW_MODE) && !ISSET(MULTIBUFFER))
687 statusbar(_("Key illegal in non-multibuffer mode"));
Chris Allegretta32da4562002-01-02 15:12:21 +0000688 else
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000689#endif
David Lawrence Ramseybe908f62004-10-01 18:34:30 +0000690 do_insertfile(
691#ifndef NANO_SMALL
692 FALSE
693#endif
694 );
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000695
696 display_main_list();
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000697}
698
Chris Allegretta355fbe52001-07-14 19:32:47 +0000699#ifdef ENABLE_MULTIBUFFER
Chris Allegretta6df90f52002-07-19 01:08:59 +0000700/* Create a new openfilestruct node. */
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000701openfilestruct *make_new_opennode(void)
Chris Allegretta6df90f52002-07-19 01:08:59 +0000702{
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000703 openfilestruct *newnode =
704 (openfilestruct *)nmalloc(sizeof(openfilestruct));
Chris Allegretta6df90f52002-07-19 01:08:59 +0000705 newnode->filename = NULL;
Chris Allegretta6df90f52002-07-19 01:08:59 +0000706 return newnode;
707}
708
709/* Splice a node into an existing openfilestruct. */
710void splice_opennode(openfilestruct *begin, openfilestruct *newnode,
David Lawrence Ramseyf7080372004-07-08 17:15:10 +0000711 openfilestruct *end)
Chris Allegretta6df90f52002-07-19 01:08:59 +0000712{
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000713 assert(newnode != NULL && begin != NULL);
David Lawrence Ramsey23c44502005-01-27 20:49:07 +0000714
Chris Allegretta6df90f52002-07-19 01:08:59 +0000715 newnode->next = end;
716 newnode->prev = begin;
717 begin->next = newnode;
718 if (end != NULL)
719 end->prev = newnode;
720}
721
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000722/* Unlink a node from the rest of the openfilestruct, and delete it. */
723void unlink_opennode(openfilestruct *fileptr)
Chris Allegretta6df90f52002-07-19 01:08:59 +0000724{
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000725 assert(fileptr != NULL && fileptr->prev != NULL && fileptr->next != NULL && fileptr != fileptr->prev && fileptr != fileptr->next);
David Lawrence Ramsey23c44502005-01-27 20:49:07 +0000726
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000727 fileptr->prev->next = fileptr->next;
728 fileptr->next->prev = fileptr->prev;
729 delete_opennode(fileptr);
Chris Allegretta6df90f52002-07-19 01:08:59 +0000730}
731
732/* Delete a node from the openfilestruct. */
733void delete_opennode(openfilestruct *fileptr)
734{
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000735 assert(fileptr != NULL && fileptr->filename != NULL && fileptr->fileage != NULL);
David Lawrence Ramsey23c44502005-01-27 20:49:07 +0000736
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000737 free(fileptr->filename);
738 free_filestruct(fileptr->fileage);
739 free(fileptr);
Chris Allegretta6df90f52002-07-19 01:08:59 +0000740}
741
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000742#ifdef DEBUG
743/* Deallocate all memory associated with this and later files, including
744 * the lines of text. */
Chris Allegretta6df90f52002-07-19 01:08:59 +0000745void free_openfilestruct(openfilestruct *src)
746{
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000747 assert(src != NULL);
David Lawrence Ramsey23c44502005-01-27 20:49:07 +0000748
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000749 while (src != src->next) {
750 src = src->next;
751 delete_opennode(src->prev);
Chris Allegretta6df90f52002-07-19 01:08:59 +0000752 }
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000753 delete_opennode(src);
Chris Allegretta6df90f52002-07-19 01:08:59 +0000754}
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000755#endif
Chris Allegretta6df90f52002-07-19 01:08:59 +0000756
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000757/* Add/update an entry to the open_files openfilestruct. If update is
David Lawrence Ramsey72e51ab2004-07-02 14:31:03 +0000758 * FALSE, a new entry is created; otherwise, the current entry is
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000759 * updated. */
David Lawrence Ramsey3e189a82004-10-03 19:18:48 +0000760void add_open_file(bool update)
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000761{
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000762 if (open_files == NULL && update)
David Lawrence Ramsey72e51ab2004-07-02 14:31:03 +0000763 return;
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000764
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000765 /* If there are no entries in open_files, make the first one. */
766 if (open_files == NULL) {
767 open_files = make_new_opennode();
768 splice_opennode(open_files, open_files, open_files);
769 /* Otherwise, if we're not updating, make a new entry for
770 * open_files and splice it in after the current entry. */
771 } else if (!update) {
772 splice_opennode(open_files, make_new_opennode(),
773 open_files->next);
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000774 open_files = open_files->next;
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000775 }
776
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000777 /* Save the current filename. */
Chris Allegrettaf2387fb2002-04-10 02:31:20 +0000778 open_files->filename = mallocstrcpy(open_files->filename, filename);
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000779
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +0000780#ifndef NANO_SMALL
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000781 /* Save the current file's stat. */
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +0000782 open_files->originalfilestat = originalfilestat;
783#endif
784
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000785 /* Save the current file buffer. */
786 open_files->fileage = fileage;
787 open_files->filebot = filebot;
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000788
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000789 /* Save the current top of the edit window. */
790 open_files->edittop = edittop;
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000791
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000792 /* Save the current line. */
793 open_files->current = current;
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000794
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000795 /* Save the current cursor position. */
796 open_files->current_x = current_x;
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000797
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000798 /* Save the current place we want. */
799 open_files->placewewant = placewewant;
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000800
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000801 /* Save the current total number of lines. */
802 open_files->totlines = totlines;
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000803
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000804 /* Save the current total size. */
805 open_files->totsize = totsize;
David Lawrence Ramseycb34a672004-02-06 21:20:05 +0000806
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000807 /* Start with no flags saved. */
808 open_files->flags = 0;
809
810 /* Save the current modification status. */
811 if (ISSET(MODIFIED))
812 open_files->flags |= MODIFIED;
813
David Lawrence Ramseya3370c42004-04-05 01:08:14 +0000814#ifndef NANO_SMALL
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000815 /* Save the current marking status and mark, if applicable. */
816 if (ISSET(MARK_ISSET)) {
817 open_files->flags |= MARK_ISSET;
818 open_files->mark_beginbuf = mark_beginbuf;
819 open_files->mark_beginx = mark_beginx;
Chris Allegretta4dc03d52002-05-11 03:04:44 +0000820 }
821
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000822 /* Save the current file format. */
823 open_files->fmt = fmt;
824#endif
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000825
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000826#ifdef DEBUG
Jordi Mallachf9390af2003-08-05 19:31:12 +0000827 fprintf(stderr, "filename is %s\n", open_files->filename);
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000828#endif
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000829}
830
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000831/* Read the current entry in the open_files structure and set up the
David Lawrence Ramsey3ece0b92004-12-01 15:11:27 +0000832 * currently open file buffer using that entry's information. */
David Lawrence Ramsey72e51ab2004-07-02 14:31:03 +0000833void load_open_file(void)
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000834{
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000835 assert(open_files != NULL);
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000836
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000837 /* Restore the current filename. */
Chris Allegrettaf2387fb2002-04-10 02:31:20 +0000838 filename = mallocstrcpy(filename, open_files->filename);
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000839
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +0000840#ifndef NANO_SMALL
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000841 /* Restore the current file's stat. */
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +0000842 originalfilestat = open_files->originalfilestat;
843#endif
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000844
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000845 /* Restore the current file buffer. */
846 fileage = open_files->fileage;
847 filebot = open_files->filebot;
848
849 /* Restore the current top of the edit window. */
850 edittop = open_files->edittop;
851
852 /* Restore the current line. */
853 current = open_files->current;
854
855 /* Restore the current cursor position. */
856 current_x = open_files->current_x;
857
858 /* Restore the current place we want. */
859 placewewant = open_files->placewewant;
860
861 /* Restore the current total number of lines. */
862 totlines = open_files->totlines;
863
864 /* Restore the current total size. */
865 totsize = open_files->totsize;
866
867 /* Restore the current modification status. */
868 if (open_files->flags & MODIFIED)
Chris Allegretta4dc03d52002-05-11 03:04:44 +0000869 SET(MODIFIED);
870 else
871 UNSET(MODIFIED);
872
873#ifndef NANO_SMALL
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000874 /* Restore the current marking status and mark, if applicable. */
875 if (open_files->flags & MARK_ISSET) {
876 mark_beginbuf = open_files->mark_beginbuf;
877 mark_beginx = open_files->mark_beginx;
Chris Allegretta4dc03d52002-05-11 03:04:44 +0000878 SET(MARK_ISSET);
879 } else
880 UNSET(MARK_ISSET);
David Lawrence Ramsey7c1f17a2004-10-03 13:47:26 +0000881
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000882 /* Restore the current file format. */
883 fmt = open_files->fmt;
Chris Allegretta4dc03d52002-05-11 03:04:44 +0000884#endif
Chris Allegrettab3655b42001-10-22 03:15:31 +0000885
Chris Allegretta7662c862003-01-13 01:35:15 +0000886#ifdef ENABLE_COLOR
887 update_color();
888#endif
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000889 edit_refresh();
Chris Allegretta7662c862003-01-13 01:35:15 +0000890
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000891 /* Update the titlebar. */
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000892 clearok(topwin, FALSE);
893 titlebar(NULL);
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000894}
895
David Lawrence Ramsey3ece0b92004-12-01 15:11:27 +0000896/* Open either the next or previous file buffer. */
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000897void open_prevnext_file(bool next)
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000898{
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000899 add_open_file(TRUE);
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000900
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000901 assert(open_files != NULL);
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000902
David Lawrence Ramsey3ece0b92004-12-01 15:11:27 +0000903 /* If only one file buffer is open, indicate it on the statusbar and
904 * get out. */
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000905 if (open_files == open_files->next) {
David Lawrence Ramsey3ece0b92004-12-01 15:11:27 +0000906 statusbar(_("No more open file buffers"));
David Lawrence Ramsey72e51ab2004-07-02 14:31:03 +0000907 return;
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000908 }
909
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000910 /* Switch to the next or previous file, depending on the value of
911 * next. */
912 open_files = next ? open_files->next : open_files->prev;
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000913
914#ifdef DEBUG
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000915 fprintf(stderr, "filename is %s\n", open_files->filename);
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000916#endif
917
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000918 /* Load the file we switched to. */
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000919 load_open_file();
920
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000921 /* And indicate the switch on the statusbar. */
Chris Allegrettaf5de33a2002-02-27 04:14:16 +0000922 statusbar(_("Switched to %s"),
David Lawrence Ramsey3e189a82004-10-03 19:18:48 +0000923 ((open_files->filename[0] == '\0') ? _("New Buffer") :
David Lawrence Ramsey72e51ab2004-07-02 14:31:03 +0000924 open_files->filename));
Chris Allegrettaf5de33a2002-02-27 04:14:16 +0000925
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000926#ifdef DEBUG
927 dump_buffer(current);
928#endif
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000929}
930
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000931/* Open the previous entry in the open_files structure. This function
932 * is used by the shortcut list. */
David Lawrence Ramsey72e51ab2004-07-02 14:31:03 +0000933void open_prevfile_void(void)
Chris Allegrettaa8c22572002-02-15 19:17:02 +0000934{
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000935 open_prevnext_file(FALSE);
Chris Allegrettaa8c22572002-02-15 19:17:02 +0000936}
937
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000938/* Open the next entry in the open_files structure. This function is
939 * used by the shortcut list. */
David Lawrence Ramsey72e51ab2004-07-02 14:31:03 +0000940void open_nextfile_void(void)
Chris Allegrettaa8c22572002-02-15 19:17:02 +0000941{
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000942 open_prevnext_file(TRUE);
Chris Allegrettaa8c22572002-02-15 19:17:02 +0000943}
944
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000945/* Delete an entry from the open_files filestruct. After deletion of an
946 * entry, the next entry is opened. Return TRUE on success or FALSE if
David Lawrence Ramsey3ece0b92004-12-01 15:11:27 +0000947 * there are no more open file buffers. */
David Lawrence Ramsey3e189a82004-10-03 19:18:48 +0000948bool close_open_file(void)
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000949{
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000950 assert(open_files != NULL);
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000951
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000952 /* If only one file is open, get out. */
953 if (open_files == open_files->next)
David Lawrence Ramsey3e189a82004-10-03 19:18:48 +0000954 return FALSE;
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000955
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000956 /* Open the next file. */
957 open_nextfile_void();
Chris Allegretta7162e3d2002-04-06 05:02:14 +0000958
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000959 /* Close the file we had open before. */
960 unlink_opennode(open_files->prev);
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000961
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000962 /* Reinitialize the shortcut list. */
David Lawrence Ramseyebd0d7c2004-07-01 18:59:52 +0000963 shortcut_init(FALSE);
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000964 display_main_list();
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000965
David Lawrence Ramsey3e189a82004-10-03 19:18:48 +0000966 return TRUE;
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000967}
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000968#endif /* ENABLE_MULTIBUFFER */
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000969
David Lawrence Ramseyca018c32004-11-26 20:14:19 +0000970#if !defined(DISABLE_SPELLER) || !defined(DISABLE_OPERATINGDIR)
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000971/*
Chris Allegrettae1f14522001-09-19 03:19:43 +0000972 * When passed "[relative path]" or "[relative path][filename]" in
973 * origpath, return "[full path]" or "[full path][filename]" on success,
974 * or NULL on error. This is still done if the file doesn't exist but
975 * the relative path does (since the file could exist in memory but not
976 * yet on disk); it is not done if the relative path doesn't exist (since
977 * the first call to chdir() will fail then).
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000978 */
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +0000979char *get_full_path(const char *origpath)
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000980{
Chris Allegrettae1f14522001-09-19 03:19:43 +0000981 char *newpath = NULL, *last_slash, *d_here, *d_there, *d_there_file, tmp;
982 int path_only, last_slash_index;
983 struct stat fileinfo;
Chris Allegrettadffa2072002-07-24 01:02:26 +0000984 char *expanded_origpath;
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000985
Chris Allegrettae1f14522001-09-19 03:19:43 +0000986 /* first, get the current directory, and tack a slash onto the end of
Chris Allegrettace78c1e2001-09-23 01:18:03 +0000987 it, unless it turns out to be "/", in which case leave it alone */
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000988
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000989 d_here = getcwd(NULL, PATH_MAX + 1);
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000990
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +0000991 if (d_here != NULL) {
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000992
993 align(&d_here);
David Lawrence Ramseyb8c479a2004-07-31 14:10:23 +0000994 if (strcmp(d_here, "/") != 0) {
David Lawrence Ramsey70047ee2003-06-14 20:41:34 +0000995 d_here = charealloc(d_here, strlen(d_here) + 2);
Chris Allegrettace78c1e2001-09-23 01:18:03 +0000996 strcat(d_here, "/");
997 }
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000998
Chris Allegrettae1f14522001-09-19 03:19:43 +0000999 /* stat origpath; if stat() fails, assume that origpath refers to
1000 a new file that hasn't been saved to disk yet (i. e. set
1001 path_only to 0); if stat() succeeds, set path_only to 0 if
1002 origpath doesn't refer to a directory, or to 1 if it does */
Chris Allegrettadffa2072002-07-24 01:02:26 +00001003 path_only = !stat(origpath, &fileinfo) && S_ISDIR(fileinfo.st_mode);
Chris Allegrettae1f14522001-09-19 03:19:43 +00001004
Chris Allegrettadffa2072002-07-24 01:02:26 +00001005 expanded_origpath = real_dir_from_tilde(origpath);
Chris Allegrettae1f14522001-09-19 03:19:43 +00001006 /* save the value of origpath in both d_there and d_there_file */
Chris Allegrettadffa2072002-07-24 01:02:26 +00001007 d_there = mallocstrcpy(NULL, expanded_origpath);
1008 d_there_file = mallocstrcpy(NULL, expanded_origpath);
1009 free(expanded_origpath);
Chris Allegretta2d7893d2001-07-11 02:08:33 +00001010
Chris Allegrettae1f14522001-09-19 03:19:43 +00001011 /* if we have a path but no filename, tack slashes onto the ends
1012 of both d_there and d_there_file, if they don't end in slashes
1013 already */
1014 if (path_only) {
1015 tmp = d_there[strlen(d_there) - 1];
1016 if (tmp != '/') {
David Lawrence Ramsey70047ee2003-06-14 20:41:34 +00001017 d_there = charealloc(d_there, strlen(d_there) + 2);
Chris Allegrettae1f14522001-09-19 03:19:43 +00001018 strcat(d_there, "/");
David Lawrence Ramsey70047ee2003-06-14 20:41:34 +00001019 d_there_file = charealloc(d_there_file, strlen(d_there_file) + 2);
Chris Allegrettae1f14522001-09-19 03:19:43 +00001020 strcat(d_there_file, "/");
1021 }
1022 }
1023
Chris Allegretta2d7893d2001-07-11 02:08:33 +00001024 /* search for the last slash in d_there */
1025 last_slash = strrchr(d_there, '/');
1026
1027 /* if we didn't find one, copy d_here into d_there; all data is
1028 then set up */
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00001029 if (last_slash == NULL)
Chris Allegrettadffa2072002-07-24 01:02:26 +00001030 d_there = mallocstrcpy(d_there, d_here);
Chris Allegretta2d7893d2001-07-11 02:08:33 +00001031 else {
Chris Allegrettae1f14522001-09-19 03:19:43 +00001032 /* otherwise, remove all non-path elements from d_there
1033 (i. e. everything after the last slash) */
Chris Allegretta2d7893d2001-07-11 02:08:33 +00001034 last_slash_index = strlen(d_there) - strlen(last_slash);
Chris Allegrettafa0c6962001-10-22 23:22:19 +00001035 null_at(&d_there, last_slash_index + 1);
Chris Allegretta2d7893d2001-07-11 02:08:33 +00001036
Chris Allegrettae1f14522001-09-19 03:19:43 +00001037 /* and remove all non-file elements from d_there_file (i. e.
1038 everything before and including the last slash); if we
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00001039 have a path but no filename, don't do anything */
Chris Allegrettae1f14522001-09-19 03:19:43 +00001040 if (!path_only) {
1041 last_slash = strrchr(d_there_file, '/');
1042 last_slash++;
1043 strcpy(d_there_file, last_slash);
1044 align(&d_there_file);
1045 }
Chris Allegretta2d7893d2001-07-11 02:08:33 +00001046
1047 /* now go to the path specified in d_there */
1048 if (chdir(d_there) != -1) {
Chris Allegrettae1f14522001-09-19 03:19:43 +00001049 /* get the full pathname, and save it back in d_there,
1050 tacking a slash on the end if we have a path but no
1051 filename; if the saving fails, get out */
Chris Allegretta2d7893d2001-07-11 02:08:33 +00001052
1053 free(d_there);
1054
Chris Allegretta2d7893d2001-07-11 02:08:33 +00001055 d_there = getcwd(NULL, PATH_MAX + 1);
Chris Allegretta2d7893d2001-07-11 02:08:33 +00001056
1057 align(&d_there);
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00001058 if (d_there != NULL) {
Chris Allegrettace78c1e2001-09-23 01:18:03 +00001059
1060 /* add a slash to d_there, unless it's "/", in which
1061 case we don't need it */
David Lawrence Ramseyb8c479a2004-07-31 14:10:23 +00001062 if (strcmp(d_there, "/") != 0) {
David Lawrence Ramsey70047ee2003-06-14 20:41:34 +00001063 d_there = charealloc(d_there, strlen(d_there) + 2);
Chris Allegrettace78c1e2001-09-23 01:18:03 +00001064 strcat(d_there, "/");
1065 }
Chris Allegrettae1f14522001-09-19 03:19:43 +00001066 }
1067 else
1068 return NULL;
Chris Allegretta2d7893d2001-07-11 02:08:33 +00001069 }
1070
1071 /* finally, go back to where we were before, d_here (no error
1072 checking is done on this chdir(), because we can do
1073 nothing if it fails) */
1074 chdir(d_here);
1075 }
1076
1077 /* all data is set up; fill in newpath */
1078
Chris Allegrettae1f14522001-09-19 03:19:43 +00001079 /* if we have a path and a filename, newpath = d_there +
1080 d_there_file; otherwise, newpath = d_there */
1081 if (!path_only) {
1082 newpath = charalloc(strlen(d_there) + strlen(d_there_file) + 1);
1083 strcpy(newpath, d_there);
1084 strcat(newpath, d_there_file);
1085 }
1086 else {
1087 newpath = charalloc(strlen(d_there) + 1);
1088 strcpy(newpath, d_there);
1089 }
Chris Allegretta2d7893d2001-07-11 02:08:33 +00001090
1091 /* finally, clean up */
1092 free(d_there_file);
1093 free(d_there);
1094 free(d_here);
1095 }
1096
1097 return newpath;
1098}
Chris Allegretta48b06702002-02-22 04:30:50 +00001099#endif /* !DISABLE_SPELLER || !DISABLE_OPERATINGDIR */
Chris Allegrettaa8c22572002-02-15 19:17:02 +00001100
1101#ifndef DISABLE_SPELLER
1102/*
Chris Allegrettabc72e362002-02-16 20:03:44 +00001103 * This function accepts a path and returns the full path (via
1104 * get_full_path()). On error, if the path doesn't reference a
1105 * directory, or if the directory isn't writable, it returns NULL.
Chris Allegrettaa8c22572002-02-15 19:17:02 +00001106 */
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001107char *check_writable_directory(const char *path)
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00001108{
Chris Allegrettaa8c22572002-02-15 19:17:02 +00001109 char *full_path = get_full_path(path);
Chris Allegrettabc72e362002-02-16 20:03:44 +00001110 int writable;
Chris Allegrettaa8c22572002-02-15 19:17:02 +00001111 struct stat fileinfo;
1112
Chris Allegrettabc72e362002-02-16 20:03:44 +00001113 /* if get_full_path() failed, return NULL */
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00001114 if (full_path == NULL)
Chris Allegrettaa8c22572002-02-15 19:17:02 +00001115 return NULL;
Chris Allegrettabc72e362002-02-16 20:03:44 +00001116
1117 /* otherwise, stat() the full path to see if it's writable by the
1118 user; set writable to 1 if it is, or 0 if it isn't */
Chris Allegrettadffa2072002-07-24 01:02:26 +00001119 writable = !stat(full_path, &fileinfo) && (fileinfo.st_mode & S_IWUSR);
Chris Allegrettaa8c22572002-02-15 19:17:02 +00001120
1121 /* if the full path doesn't end in a slash (meaning get_full_path()
Chris Allegrettabc72e362002-02-16 20:03:44 +00001122 found that it isn't a directory) or isn't writable, free full_path
1123 and return NULL */
1124 if (full_path[strlen(full_path) - 1] != '/' || writable == 0) {
1125 free(full_path);
Chris Allegrettaa8c22572002-02-15 19:17:02 +00001126 return NULL;
Chris Allegrettabc72e362002-02-16 20:03:44 +00001127 }
Chris Allegrettaa8c22572002-02-15 19:17:02 +00001128
1129 /* otherwise, return the full path */
1130 return full_path;
1131}
1132
1133/*
1134 * This function accepts a directory name and filename prefix the same
1135 * way that tempnam() does, determines the location for its temporary
1136 * file the same way that tempnam() does, safely creates the temporary
1137 * file there via mkstemp(), and returns the name of the temporary file
Chris Allegrettabc72e362002-02-16 20:03:44 +00001138 * the same way that tempnam() does. It does not reference the value of
1139 * TMP_MAX because the total number of random filenames that it can
1140 * generate using one prefix is equal to 256**6, which is a sufficiently
1141 * large number to handle most cases. Since the behavior after tempnam()
1142 * generates TMP_MAX random filenames is implementation-defined, my
1143 * implementation is to go on generating random filenames regardless of
1144 * it.
Chris Allegrettaa8c22572002-02-15 19:17:02 +00001145 */
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00001146char *safe_tempnam(const char *dirname, const char *filename_prefix)
1147{
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001148 char *full_tempdir = NULL;
1149 const char *TMPDIR_env;
Chris Allegrettabc72e362002-02-16 20:03:44 +00001150 int filedesc;
Chris Allegrettaa8c22572002-02-15 19:17:02 +00001151
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001152 /* if $TMPDIR is set and non-empty, set tempdir to it, run it through
1153 get_full_path(), and save the result in full_tempdir; otherwise,
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00001154 leave full_tempdir set to NULL */
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001155 TMPDIR_env = getenv("TMPDIR");
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00001156 if (TMPDIR_env != NULL && TMPDIR_env[0] != '\0')
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001157 full_tempdir = check_writable_directory(TMPDIR_env);
Chris Allegrettaa8c22572002-02-15 19:17:02 +00001158
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001159 /* if $TMPDIR is blank or isn't set, or isn't a writable
1160 directory, and dirname isn't NULL, try it; otherwise, leave
1161 full_tempdir set to NULL */
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00001162 if (full_tempdir == NULL && dirname != NULL)
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001163 full_tempdir = check_writable_directory(dirname);
Chris Allegrettaa8c22572002-02-15 19:17:02 +00001164
1165 /* if $TMPDIR is blank or isn't set, or if it isn't a writable
1166 directory, and dirname is NULL, try P_tmpdir instead */
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00001167 if (full_tempdir == NULL)
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001168 full_tempdir = check_writable_directory(P_tmpdir);
Chris Allegrettaa8c22572002-02-15 19:17:02 +00001169
1170 /* if P_tmpdir didn't work, use /tmp instead */
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00001171 if (full_tempdir == NULL) {
Chris Allegrettaa8c22572002-02-15 19:17:02 +00001172 full_tempdir = charalloc(6);
1173 strcpy(full_tempdir, "/tmp/");
1174 }
1175
David Lawrence Ramsey70047ee2003-06-14 20:41:34 +00001176 full_tempdir = charealloc(full_tempdir, strlen(full_tempdir) + 12);
Chris Allegrettaa8c22572002-02-15 19:17:02 +00001177
1178 /* like tempnam(), use only the first 5 characters of the prefix */
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001179 strncat(full_tempdir, filename_prefix, 5);
1180 strcat(full_tempdir, "XXXXXX");
1181 filedesc = mkstemp(full_tempdir);
Chris Allegrettaa8c22572002-02-15 19:17:02 +00001182
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001183 /* if mkstemp succeeded, close the resulting file; afterwards, it'll be
1184 0 bytes long, so delete it; finally, return the filename (all that's
1185 left of it) */
1186 if (filedesc != -1) {
Chris Allegrettaa8c22572002-02-15 19:17:02 +00001187 close(filedesc);
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001188 unlink(full_tempdir);
1189 return full_tempdir;
Chris Allegrettaa8c22572002-02-15 19:17:02 +00001190 }
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001191
1192 free(full_tempdir);
1193 return NULL;
Chris Allegrettaa8c22572002-02-15 19:17:02 +00001194}
1195#endif /* !DISABLE_SPELLER */
Chris Allegrettae1f14522001-09-19 03:19:43 +00001196
1197#ifndef DISABLE_OPERATINGDIR
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001198/* Initialize full_operating_dir based on operating_dir. */
1199void init_operating_dir(void)
1200{
1201 assert(full_operating_dir == NULL);
1202
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00001203 if (operating_dir == NULL)
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001204 return;
David Lawrence Ramsey807681a2004-02-27 20:50:01 +00001205
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001206 full_operating_dir = get_full_path(operating_dir);
1207
1208 /* If get_full_path() failed or the operating directory is
David Lawrence Ramsey95b7bdb2004-02-27 03:21:23 +00001209 * inaccessible, unset operating_dir. */
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00001210 if (full_operating_dir == NULL || chdir(full_operating_dir) == -1) {
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001211 free(full_operating_dir);
1212 full_operating_dir = NULL;
1213 free(operating_dir);
1214 operating_dir = NULL;
1215 }
1216}
1217
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001218/* Check to see if we're inside the operating directory. Return 0 if we
Chris Allegrettae1f14522001-09-19 03:19:43 +00001219 * are, or 1 otherwise. If allow_tabcomp is nonzero, allow incomplete
1220 * names that would be matches for the operating directory, so that tab
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001221 * completion will work. */
David Lawrence Ramsey3e819142004-12-31 04:10:28 +00001222int check_operating_dir(const char *currpath, bool allow_tabcomp)
Chris Allegrettae1f14522001-09-19 03:19:43 +00001223{
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001224 /* The char *full_operating_dir is global for mem cleanup. It
1225 * should have already been initialized by init_operating_dir().
1226 * Also, a relative operating directory path will only be handled
1227 * properly if this is done. */
Chris Allegrettae1f14522001-09-19 03:19:43 +00001228
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001229 char *fullpath;
1230 int retval = 0;
1231 const char *whereami1, *whereami2 = NULL;
Chris Allegrettae1f14522001-09-19 03:19:43 +00001232
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001233 /* If no operating directory is set, don't bother doing anything. */
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00001234 if (operating_dir == NULL)
Chris Allegrettae1f14522001-09-19 03:19:43 +00001235 return 0;
David Lawrence Ramsey6a2877d2004-02-28 02:08:15 +00001236
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001237 assert(full_operating_dir != NULL);
Chris Allegrettae1f14522001-09-19 03:19:43 +00001238
Chris Allegrettae1f14522001-09-19 03:19:43 +00001239 fullpath = get_full_path(currpath);
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001240
1241 /* fullpath == NULL means some directory in the path doesn't exist
1242 * or is unreadable. If allow_tabcomp is zero, then currpath is
1243 * what the user typed somewhere. We don't want to report a
1244 * non-existent directory as being outside the operating directory,
1245 * so we return 0. If allow_tabcomp is nonzero, then currpath
1246 * exists, but is not executable. So we say it isn't in the
1247 * operating directory. */
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00001248 if (fullpath == NULL)
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001249 return allow_tabcomp;
Chris Allegrettae1f14522001-09-19 03:19:43 +00001250
1251 whereami1 = strstr(fullpath, full_operating_dir);
1252 if (allow_tabcomp)
1253 whereami2 = strstr(full_operating_dir, fullpath);
1254
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001255 /* If both searches failed, we're outside the operating directory.
1256 * Otherwise, check the search results; if the full operating
1257 * directory path is not at the beginning of the full current path
1258 * (for normal usage) and vice versa (for tab completion, if we're
1259 * allowing it), we're outside the operating directory. */
Chris Allegrettae1f14522001-09-19 03:19:43 +00001260 if (whereami1 != fullpath && whereami2 != full_operating_dir)
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001261 retval = 1;
1262 free(fullpath);
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001263
1264 /* Otherwise, we're still inside it. */
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001265 return retval;
Chris Allegrettae1f14522001-09-19 03:19:43 +00001266}
Chris Allegretta2d7893d2001-07-11 02:08:33 +00001267#endif
1268
David Lawrence Ramsey04e42a62004-02-28 16:24:31 +00001269#ifndef NANO_SMALL
1270void init_backup_dir(void)
1271{
1272 char *full_backup_dir;
1273
1274 if (backup_dir == NULL)
1275 return;
1276
1277 full_backup_dir = get_full_path(backup_dir);
1278
1279 /* If get_full_path() failed or the backup directory is
1280 * inaccessible, unset backup_dir. */
1281 if (full_backup_dir == NULL ||
1282 full_backup_dir[strlen(full_backup_dir) - 1] != '/') {
1283 free(full_backup_dir);
1284 free(backup_dir);
1285 backup_dir = NULL;
1286 } else {
1287 free(backup_dir);
1288 backup_dir = full_backup_dir;
1289 }
1290}
1291#endif
1292
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001293/* Read from inn, write to out. We assume inn is opened for reading,
1294 * and out for writing. We return 0 on success, -1 on read error, -2 on
1295 * write error. */
1296int copy_file(FILE *inn, FILE *out)
1297{
1298 char buf[BUFSIZ];
1299 size_t charsread;
1300 int retval = 0;
1301
1302 assert(inn != NULL && out != NULL);
1303 do {
1304 charsread = fread(buf, sizeof(char), BUFSIZ, inn);
1305 if (charsread == 0 && ferror(inn)) {
1306 retval = -1;
1307 break;
1308 }
1309 if (fwrite(buf, sizeof(char), charsread, out) < charsread) {
1310 retval = -2;
1311 break;
1312 }
1313 } while (charsread > 0);
1314 if (fclose(inn) == EOF)
1315 retval = -1;
1316 if (fclose(out) == EOF)
1317 retval = -2;
1318 return retval;
1319}
1320
David Lawrence Ramseyc8c69d52004-07-03 03:22:23 +00001321/* Write a file out. If tmp is FALSE, we set the umask to disallow
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001322 * anyone else from accessing the file, we don't set the global variable
1323 * filename to its name, and we don't print out how many lines we wrote
1324 * on the statusbar.
Chris Allegretta7662c862003-01-13 01:35:15 +00001325 *
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001326 * tmp means we are writing a temporary file in a secure fashion. We
1327 * use it when spell checking or dumping the file on an error.
Chris Allegrettacc197ef2001-05-29 04:21:44 +00001328 *
Chris Allegretta0e9b7aa2002-04-16 03:15:47 +00001329 * append == 1 means we are appending instead of overwriting.
1330 * append == 2 means we are prepending instead of overwriting.
Chris Allegrettaecc3d7f2001-06-05 23:24:55 +00001331 *
David Lawrence Ramsey2f0d03b2004-05-28 00:15:28 +00001332 * nonamechange means don't change the current filename. It is ignored
1333 * if tmp is FALSE or if we're appending/prepending.
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001334 *
1335 * Return -1 on error, 1 on success. */
David Lawrence Ramsey8c16bac2004-11-06 15:10:57 +00001336int write_file(const char *name, bool tmp, int append, bool
1337 nonamechange)
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001338{
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001339 int retval = -1;
1340 /* Instead of returning in this function, you should always
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001341 * merely set retval and then goto cleanup_and_exit. */
1342 size_t lineswritten = 0;
1343 const filestruct *fileptr = fileage;
Chris Allegretta0547eb32002-04-22 23:52:34 +00001344 int fd;
David Lawrence Ramsey0af86042004-07-07 23:26:08 +00001345 /* The file descriptor we use. */
David Lawrence Ramsey25529072004-07-06 14:02:44 +00001346 mode_t original_umask = 0;
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001347 /* Our umask, from when nano started. */
David Lawrence Ramsey8c16bac2004-11-06 15:10:57 +00001348 bool realexists;
David Lawrence Ramseyc8c69d52004-07-03 03:22:23 +00001349 /* The result of stat(). TRUE if the file exists, FALSE
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001350 * otherwise. If name is a link that points nowhere, realexists
David Lawrence Ramseyc8c69d52004-07-03 03:22:23 +00001351 * is FALSE. */
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001352 struct stat st;
1353 /* The status fields filled in by stat(). */
David Lawrence Ramsey8c16bac2004-11-06 15:10:57 +00001354 bool anyexists;
David Lawrence Ramsey0af86042004-07-07 23:26:08 +00001355 /* The result of lstat(). Same as realexists unless name is a
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001356 * link. */
1357 struct stat lst;
1358 /* The status fields filled in by lstat(). */
1359 char *realname;
David Lawrence Ramsey0af86042004-07-07 23:26:08 +00001360 /* name after tilde expansion. */
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001361 FILE *f;
1362 /* The actual file, realname, we are writing to. */
1363 char *tempname = NULL;
1364 /* The temp file name we write to on prepend. */
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001365
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001366 assert(name != NULL);
David Lawrence Ramsey951d7142004-10-04 15:23:47 +00001367 if (name[0] == '\0')
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001368 return -1;
Chris Allegrettaa0d89972003-02-03 03:32:08 +00001369 if (!tmp)
1370 titlebar(NULL);
Chris Allegretta0f5dfef2000-11-24 14:02:57 +00001371
Chris Allegrettabe77c612000-11-24 14:00:16 +00001372 realname = real_dir_from_tilde(name);
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001373
Chris Allegrettae1f14522001-09-19 03:19:43 +00001374#ifndef DISABLE_OPERATINGDIR
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001375 /* If we're writing a temporary file, we're probably going outside
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001376 * the operating directory, so skip the operating directory test. */
David Lawrence Ramsey72e51ab2004-07-02 14:31:03 +00001377 if (!tmp && check_operating_dir(realname, FALSE) != 0) {
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001378 statusbar(_("Can't write outside of %s"), operating_dir);
1379 goto cleanup_and_exit;
Chris Allegrettae1f14522001-09-19 03:19:43 +00001380 }
1381#endif
1382
David Lawrence Ramsey8c16bac2004-11-06 15:10:57 +00001383 anyexists = (lstat(realname, &lst) != -1);
David Lawrence Ramseye08765d2004-11-26 20:17:49 +00001384
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001385 /* New case: if the file exists, just give up. */
1386 if (tmp && anyexists)
1387 goto cleanup_and_exit;
David Lawrence Ramseye08765d2004-11-26 20:17:49 +00001388
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001389 /* If NOFOLLOW_SYMLINKS is set, it doesn't make sense to prepend or
1390 * append to a symlink. Here we warn about the contradiction. */
1391 if (ISSET(NOFOLLOW_SYMLINKS) && anyexists && S_ISLNK(lst.st_mode)) {
David Lawrence Ramseyc8c69d52004-07-03 03:22:23 +00001392 statusbar(
1393 _("Cannot prepend or append to a symlink with --nofollow set"));
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001394 goto cleanup_and_exit;
1395 }
1396
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001397 /* Save the state of file at the end of the symlink (if there is
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001398 * one). */
David Lawrence Ramsey8c16bac2004-11-06 15:10:57 +00001399 realexists = (stat(realname, &st) != -1);
Chris Allegrettaf7ee9e62000-12-02 21:13:50 +00001400
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +00001401#ifndef NANO_SMALL
1402 /* We backup only if the backup toggle is set, the file isn't
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001403 * temporary, and the file already exists. Furthermore, if we
1404 * aren't appending, prepending, or writing a selection, we backup
1405 * only if the file has not been modified by someone else since nano
1406 * opened it. */
David Lawrence Ramsey8c16bac2004-11-06 15:10:57 +00001407 if (ISSET(BACKUP_FILE) && !tmp && realexists &&
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001408 (append != 0 || ISSET(MARK_ISSET) ||
1409 originalfilestat.st_mtime == st.st_mtime)) {
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +00001410
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001411 FILE *backup_file;
1412 char *backupname;
1413 struct utimbuf filetime;
1414 int copy_status;
1415
1416 /* Save the original file's access and modification times. */
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +00001417 filetime.actime = originalfilestat.st_atime;
1418 filetime.modtime = originalfilestat.st_mtime;
1419
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001420 /* Open the original file to copy to the backup. */
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +00001421 f = fopen(realname, "rb");
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00001422 if (f == NULL) {
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001423 statusbar(_("Error reading %s: %s"), realname,
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +00001424 strerror(errno));
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001425 goto cleanup_and_exit;
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +00001426 }
1427
David Lawrence Ramsey04e42a62004-02-28 16:24:31 +00001428 /* If backup_dir is set, we set backupname to
1429 * backup_dir/backupname~, where backupnae is the canonicalized
1430 * absolute pathname of realname with every '/' replaced with a
1431 * '!'. This means that /home/foo/file is backed up in
1432 * backup_dir/!home!foo!file~. */
1433 if (backup_dir != NULL) {
1434 char *canon_realname = get_full_path(realname);
1435 size_t i;
1436
1437 if (canon_realname == NULL)
1438 /* If get_full_path() failed, we don't have a
1439 * canonicalized absolute pathname, so just use the
1440 * filename portion of the pathname. We use tail() so
1441 * that e.g. ../backupname will be backed up in
1442 * backupdir/backupname~ instead of
1443 * backupdir/../backupname~. */
1444 canon_realname = mallocstrcpy(NULL, tail(realname));
1445 else {
1446 for (i = 0; canon_realname[i] != '\0'; i++) {
1447 if (canon_realname[i] == '/')
1448 canon_realname[i] = '!';
1449 }
1450 }
1451
1452 backupname = charalloc(strlen(backup_dir) +
1453 strlen(canon_realname) + 2);
1454 sprintf(backupname, "%s%s~", backup_dir, canon_realname);
1455 free(canon_realname);
1456 } else {
1457 backupname = charalloc(strlen(realname) + 2);
1458 sprintf(backupname, "%s~", realname);
1459 }
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +00001460
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001461 /* Open the destination backup file. Before we write to it, we
1462 * set its permissions, so no unauthorized person can read it as
1463 * we write. */
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +00001464 backup_file = fopen(backupname, "wb");
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001465 if (backup_file == NULL ||
1466 chmod(backupname, originalfilestat.st_mode) == -1) {
David Lawrence Ramsey2dbcc802004-11-26 18:10:07 +00001467 statusbar(_("Error writing %s: %s"), backupname,
1468 strerror(errno));
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +00001469 free(backupname);
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001470 if (backup_file != NULL)
1471 fclose(backup_file);
1472 fclose(f);
1473 goto cleanup_and_exit;
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +00001474 }
1475
1476#ifdef DEBUG
Jordi Mallachf9390af2003-08-05 19:31:12 +00001477 fprintf(stderr, "Backing up %s to %s\n", realname, backupname);
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +00001478#endif
1479
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001480 /* Copy the file. */
1481 copy_status = copy_file(f, backup_file);
David Lawrence Ramseye08765d2004-11-26 20:17:49 +00001482
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001483 /* And set metadata. */
David Lawrence Ramsey2dbcc802004-11-26 18:10:07 +00001484 if (copy_status != 0 || chown(backupname,
1485 originalfilestat.st_uid, originalfilestat.st_gid) == -1
1486 || utime(backupname, &filetime) == -1) {
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001487 free(backupname);
1488 if (copy_status == -1)
David Lawrence Ramsey2dbcc802004-11-26 18:10:07 +00001489 statusbar(_("Error reading %s: %s"), realname,
1490 strerror(errno));
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001491 else
1492 statusbar(_("Error writing %s: %s"), backupname,
1493 strerror(errno));
1494 goto cleanup_and_exit;
1495 }
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +00001496 free(backupname);
1497 }
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001498#endif /* !NANO_SMALL */
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +00001499
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001500 /* If NOFOLLOW_SYMLINKS and the file is a link, we aren't doing
1501 * prepend or append. So we delete the link first, and just
1502 * overwrite. */
1503 if (ISSET(NOFOLLOW_SYMLINKS) && anyexists && S_ISLNK(lst.st_mode) &&
1504 unlink(realname) == -1) {
1505 statusbar(_("Error writing %s: %s"), realname, strerror(errno));
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001506 goto cleanup_and_exit;
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001507 }
Chris Allegretta07f9ee02000-12-04 05:15:39 +00001508
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001509 original_umask = umask(0);
1510 umask(original_umask);
David Lawrence Ramseyc8c69d52004-07-03 03:22:23 +00001511
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001512 /* If we create a temp file, we don't let anyone else access it. We
David Lawrence Ramseyc8c69d52004-07-03 03:22:23 +00001513 * create a temp file if tmp is TRUE or if we're prepending. */
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001514 if (tmp || append == 2)
1515 umask(S_IRWXG | S_IRWXO);
1516
David Lawrence Ramseyc8c69d52004-07-03 03:22:23 +00001517 /* If we're prepending, copy the file to a temp file. */
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001518 if (append == 2) {
1519 int fd_source;
1520 FILE *f_source = NULL;
1521
1522 tempname = charalloc(strlen(realname) + 8);
1523 strcpy(tempname, realname);
1524 strcat(tempname, ".XXXXXX");
1525 fd = mkstemp(tempname);
1526 f = NULL;
1527 if (fd != -1) {
1528 f = fdopen(fd, "wb");
1529 if (f == NULL)
1530 close(fd);
1531 }
1532 if (f == NULL) {
David Lawrence Ramsey2dbcc802004-11-26 18:10:07 +00001533 statusbar(_("Error writing %s: %s"), tempname,
1534 strerror(errno));
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001535 unlink(tempname);
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001536 goto cleanup_and_exit;
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001537 }
Chris Allegretta07f9ee02000-12-04 05:15:39 +00001538
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001539 fd_source = open(realname, O_RDONLY | O_CREAT);
1540 if (fd_source != -1) {
1541 f_source = fdopen(fd_source, "rb");
1542 if (f_source == NULL)
1543 close(fd_source);
1544 }
1545 if (f_source == NULL) {
David Lawrence Ramsey2dbcc802004-11-26 18:10:07 +00001546 statusbar(_("Error reading %s: %s"), realname,
1547 strerror(errno));
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001548 fclose(f);
1549 unlink(tempname);
1550 goto cleanup_and_exit;
1551 }
1552
1553 if (copy_file(f_source, f) != 0) {
David Lawrence Ramsey2dbcc802004-11-26 18:10:07 +00001554 statusbar(_("Error writing %s: %s"), tempname,
1555 strerror(errno));
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001556 unlink(tempname);
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001557 goto cleanup_and_exit;
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001558 }
1559 }
1560
David Lawrence Ramseyc8c69d52004-07-03 03:22:23 +00001561 /* Now open the file in place. Use O_EXCL if tmp is TRUE. This is
1562 * now copied from joe, because wiggy says so *shrug*. */
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001563 fd = open(realname, O_WRONLY | O_CREAT |
1564 (append == 1 ? O_APPEND : (tmp ? O_EXCL : O_TRUNC)),
David Lawrence Ramseyc8c69d52004-07-03 03:22:23 +00001565 S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001566
David Lawrence Ramseyc8c69d52004-07-03 03:22:23 +00001567 /* Set the umask back to the user's original value. */
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001568 umask(original_umask);
1569
1570 /* First, just give up if we couldn't even open the file. */
1571 if (fd == -1) {
1572 statusbar(_("Error writing %s: %s"), realname, strerror(errno));
David Lawrence Ramseyca92bec2004-10-22 03:33:54 +00001573 /* tempname has been set only if we're prepending. */
1574 if (tempname != NULL)
1575 unlink(tempname);
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001576 goto cleanup_and_exit;
1577 }
Chris Allegretta0547eb32002-04-22 23:52:34 +00001578
David Lawrence Ramseyf21cd102002-06-13 00:40:19 +00001579 f = fdopen(fd, append == 1 ? "ab" : "wb");
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00001580 if (f == NULL) {
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001581 statusbar(_("Error writing %s: %s"), realname, strerror(errno));
1582 close(fd);
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001583 goto cleanup_and_exit;
Chris Allegretta0547eb32002-04-22 23:52:34 +00001584 }
1585
David Lawrence Ramsey32d19ce2004-05-24 05:05:07 +00001586 /* There might not be a magicline. There won't be when writing out
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001587 * a selection. */
1588 assert(fileage != NULL && filebot != NULL);
1589 while (fileptr != filebot) {
1590 size_t data_len = strlen(fileptr->data);
1591 size_t size;
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001592
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001593 /* Newlines to nulls, just before we write to disk. */
David Lawrence Ramseyf21cd102002-06-13 00:40:19 +00001594 sunder(fileptr->data);
1595
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001596 size = fwrite(fileptr->data, sizeof(char), data_len, f);
David Lawrence Ramseyf21cd102002-06-13 00:40:19 +00001597
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001598 /* Nulls to newlines; data_len is the string's real length. */
David Lawrence Ramseyf21cd102002-06-13 00:40:19 +00001599 unsunder(fileptr->data, data_len);
1600
Chris Allegretta0547eb32002-04-22 23:52:34 +00001601 if (size < data_len) {
David Lawrence Ramsey2dbcc802004-11-26 18:10:07 +00001602 statusbar(_("Error writing %s: %s"), realname,
1603 strerror(errno));
Chris Allegrettab2751752002-04-23 10:22:33 +00001604 fclose(f);
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001605 goto cleanup_and_exit;
Chris Allegrettab2751752002-04-23 10:22:33 +00001606 }
Chris Allegretta91841892001-09-21 02:37:01 +00001607#ifndef NANO_SMALL
David Lawrence Ramsey2dbcc802004-11-26 18:10:07 +00001608 if (fmt == DOS_FILE || fmt == MAC_FILE) {
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001609 if (putc('\r', f) == EOF) {
David Lawrence Ramsey2dbcc802004-11-26 18:10:07 +00001610 statusbar(_("Error writing %s: %s"), realname,
1611 strerror(errno));
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001612 fclose(f);
1613 goto cleanup_and_exit;
1614 }
David Lawrence Ramsey2dbcc802004-11-26 18:10:07 +00001615 }
Chris Allegretta8fa1e282001-09-22 04:20:25 +00001616
David Lawrence Ramsey2dbcc802004-11-26 18:10:07 +00001617 if (fmt != MAC_FILE) {
Chris Allegretta91841892001-09-21 02:37:01 +00001618#endif
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001619 if (putc('\n', f) == EOF) {
David Lawrence Ramsey2dbcc802004-11-26 18:10:07 +00001620 statusbar(_("Error writing %s: %s"), realname,
1621 strerror(errno));
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001622 fclose(f);
1623 goto cleanup_and_exit;
1624 }
David Lawrence Ramsey2dbcc802004-11-26 18:10:07 +00001625#ifndef NANO_SMALL
1626 }
1627#endif
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001628
1629 fileptr = fileptr->next;
1630 lineswritten++;
1631 }
1632
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001633 /* If we're prepending, open the temp file, and append it to f. */
1634 if (append == 2) {
1635 int fd_source;
1636 FILE *f_source = NULL;
David Lawrence Ramseyf21cd102002-06-13 00:40:19 +00001637
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001638 fd_source = open(tempname, O_RDONLY | O_CREAT);
1639 if (fd_source != -1) {
1640 f_source = fdopen(fd_source, "rb");
1641 if (f_source == NULL)
1642 close(fd_source);
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001643 }
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001644 if (f_source == NULL) {
David Lawrence Ramsey2dbcc802004-11-26 18:10:07 +00001645 statusbar(_("Error reading %s: %s"), tempname,
1646 strerror(errno));
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001647 fclose(f);
1648 goto cleanup_and_exit;
1649 }
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001650
David Lawrence Ramsey2c4e34c2004-10-22 03:30:39 +00001651 if (copy_file(f_source, f) == -1 || unlink(tempname) == -1) {
David Lawrence Ramsey2dbcc802004-11-26 18:10:07 +00001652 statusbar(_("Error writing %s: %s"), realname,
1653 strerror(errno));
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001654 goto cleanup_and_exit;
1655 }
1656 } else if (fclose(f) == EOF) {
1657 statusbar(_("Error writing %s: %s"), realname, strerror(errno));
1658 unlink(tempname);
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001659 goto cleanup_and_exit;
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001660 }
1661
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00001662 if (!tmp && append == 0) {
Chris Allegretta7662c862003-01-13 01:35:15 +00001663 if (!nonamechange) {
Chris Allegrettaecc3d7f2001-06-05 23:24:55 +00001664 filename = mallocstrcpy(filename, realname);
Chris Allegretta7662c862003-01-13 01:35:15 +00001665#ifdef ENABLE_COLOR
1666 update_color();
David Lawrence Ramsey760a2dc2004-01-14 06:38:00 +00001667 if (ISSET(COLOR_SYNTAX))
1668 edit_refresh();
Chris Allegretta7662c862003-01-13 01:35:15 +00001669#endif
1670 }
Chris Allegrettaecc3d7f2001-06-05 23:24:55 +00001671
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +00001672#ifndef NANO_SMALL
1673 /* Update originalfilestat to reference the file as it is now. */
1674 stat(filename, &originalfilestat);
1675#endif
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001676 statusbar(P_("Wrote %u line", "Wrote %u lines", lineswritten),
1677 lineswritten);
Chris Allegretta1bd0ce22000-12-06 05:56:08 +00001678 UNSET(MODIFIED);
Chris Allegrettaf4b96012001-01-03 07:11:47 +00001679 titlebar(NULL);
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001680 }
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001681
1682 retval = 1;
1683
1684 cleanup_and_exit:
1685 free(realname);
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001686 free(tempname);
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001687 return retval;
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001688}
1689
David Lawrence Ramsey35961c42004-01-23 19:34:03 +00001690#ifndef NANO_SMALL
1691/* Write a marked selection from a file out. First, set fileage and
1692 * filebot as the top and bottom of the mark, respectively. Then call
David Lawrence Ramsey2f0d03b2004-05-28 00:15:28 +00001693 * write_file() with the values of name, temp, and append, and with
1694 * nonamechange set to TRUE so that we don't change the current
1695 * filename. Finally, set fileage and filebot back to their old values
1696 * and return. */
David Lawrence Ramsey8c16bac2004-11-06 15:10:57 +00001697int write_marked(const char *name, bool tmp, int append)
David Lawrence Ramsey35961c42004-01-23 19:34:03 +00001698{
1699 int retval = -1;
David Lawrence Ramsey7cfea8b2004-10-08 15:35:33 +00001700 bool old_modified = ISSET(MODIFIED);
David Lawrence Ramsey35961c42004-01-23 19:34:03 +00001701 /* write_file() unsets the MODIFIED flag. */
David Lawrence Ramsey40bdb682004-11-03 22:03:41 +00001702 bool added_magicline;
1703 /* Whether we added a magicline after filebot. */
1704 filestruct *top, *bot;
1705 size_t top_x, bot_x;
David Lawrence Ramsey35961c42004-01-23 19:34:03 +00001706
David Lawrence Ramsey40bdb682004-11-03 22:03:41 +00001707 /* Partition the filestruct so that it contains only the marked
1708 * text. */
1709 mark_order((const filestruct **)&top, &top_x,
David Lawrence Ramsey90e59c12004-11-05 23:03:03 +00001710 (const filestruct **)&bot, &bot_x, NULL);
David Lawrence Ramsey40bdb682004-11-03 22:03:41 +00001711 filepart = partition_filestruct(top, top_x, bot, bot_x);
David Lawrence Ramsey35961c42004-01-23 19:34:03 +00001712
1713 /* If the line at filebot is blank, treat it as the magicline and
David Lawrence Ramsey40bdb682004-11-03 22:03:41 +00001714 * hence the end of the file. Otherwise, add a magicline and treat
1715 * it as the end of the file. */
1716 added_magicline = (filebot->data[0] != '\0');
1717 if (added_magicline)
1718 new_magicline();
David Lawrence Ramsey35961c42004-01-23 19:34:03 +00001719
David Lawrence Ramsey2f0d03b2004-05-28 00:15:28 +00001720 retval = write_file(name, tmp, append, TRUE);
David Lawrence Ramsey35961c42004-01-23 19:34:03 +00001721
David Lawrence Ramsey40bdb682004-11-03 22:03:41 +00001722 /* If we added a magicline, remove it now. */
1723 if (added_magicline)
1724 remove_magicline();
1725
1726 /* Unpartition the filestruct so that it contains all the text
1727 * again. */
David Lawrence Ramsey74d87072004-11-22 00:16:23 +00001728 unpartition_filestruct(&filepart);
David Lawrence Ramsey40bdb682004-11-03 22:03:41 +00001729
David Lawrence Ramsey7cfea8b2004-10-08 15:35:33 +00001730 if (old_modified)
David Lawrence Ramsey35961c42004-01-23 19:34:03 +00001731 set_modified();
1732
1733 return retval;
1734}
1735#endif /* !NANO_SMALL */
1736
David Lawrence Ramsey951d7142004-10-04 15:23:47 +00001737int do_writeout(bool exiting)
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001738{
David Lawrence Ramsey6aec4b82004-03-15 20:26:30 +00001739 int i;
David Lawrence Ramsey045883a2004-10-05 20:11:31 +00001740 int retval = 0, append = 0;
1741 char *ans;
1742 /* The last answer the user typed on the statusbar. */
Chris Allegretta8a0de3b2000-11-24 20:45:14 +00001743#ifdef NANO_EXTRA
David Lawrence Ramsey056c5ef2004-10-05 16:14:19 +00001744 static bool did_cred = FALSE;
Chris Allegretta8a0de3b2000-11-24 20:45:14 +00001745#endif
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001746
Chris Allegretta6b58acd2001-04-12 03:01:53 +00001747 currshortcut = writefile_list;
Chris Allegretta6fe61492001-05-21 12:56:25 +00001748
David Lawrence Ramseyedab0cc2004-07-03 03:09:12 +00001749 if (exiting && filename[0] != '\0' && ISSET(TEMP_FILE)) {
David Lawrence Ramsey045883a2004-10-05 20:11:31 +00001750 retval = write_file(filename, FALSE, 0, FALSE);
1751
1752 /* Write succeeded. */
1753 if (retval == 1)
1754 return retval;
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001755 }
1756
Chris Allegretta500b5e32001-06-21 23:58:47 +00001757#ifndef NANO_SMALL
David Lawrence Ramsey6aec4b82004-03-15 20:26:30 +00001758 if (ISSET(MARK_ISSET) && !exiting)
David Lawrence Ramsey045883a2004-10-05 20:11:31 +00001759 ans = mallocstrcpy(NULL, "");
David Lawrence Ramsey6aec4b82004-03-15 20:26:30 +00001760 else
1761#endif
David Lawrence Ramsey045883a2004-10-05 20:11:31 +00001762 ans = mallocstrcpy(NULL, filename);
David Lawrence Ramsey6aec4b82004-03-15 20:26:30 +00001763
1764 while (TRUE) {
1765 const char *msg;
1766#ifndef NANO_SMALL
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001767 const char *formatstr, *backupstr;
1768
David Lawrence Ramsey3e0c8a62004-11-04 04:08:18 +00001769 if (fmt == DOS_FILE)
David Lawrence Ramsey576bf332004-07-12 03:10:30 +00001770 formatstr = N_(" [DOS Format]");
David Lawrence Ramsey3e0c8a62004-11-04 04:08:18 +00001771 else if (fmt == MAC_FILE)
David Lawrence Ramsey360191c2004-10-01 21:37:36 +00001772 formatstr = N_(" [Mac Format]");
Chris Allegrettaa8c22572002-02-15 19:17:02 +00001773 else
1774 formatstr = "";
1775
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +00001776 if (ISSET(BACKUP_FILE))
David Lawrence Ramsey576bf332004-07-12 03:10:30 +00001777 backupstr = N_(" [Backup]");
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +00001778 else
1779 backupstr = "";
1780
David Lawrence Ramsey6aec4b82004-03-15 20:26:30 +00001781 /* Be nice to the translation folks. */
Chris Allegretta9519eb02001-09-27 20:46:25 +00001782 if (ISSET(MARK_ISSET) && !exiting) {
Chris Allegretta0e9b7aa2002-04-16 03:15:47 +00001783 if (append == 2)
David Lawrence Ramsey576bf332004-07-12 03:10:30 +00001784 msg = N_("Prepend Selection to File");
Chris Allegretta7662c862003-01-13 01:35:15 +00001785 else if (append == 1)
David Lawrence Ramsey576bf332004-07-12 03:10:30 +00001786 msg = N_("Append Selection to File");
Chris Allegretta9519eb02001-09-27 20:46:25 +00001787 else
David Lawrence Ramsey576bf332004-07-12 03:10:30 +00001788 msg = N_("Write Selection to File");
David Lawrence Ramsey6aec4b82004-03-15 20:26:30 +00001789 } else
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +00001790#endif /* !NANO_SMALL */
David Lawrence Ramsey6aec4b82004-03-15 20:26:30 +00001791 if (append == 2)
David Lawrence Ramsey576bf332004-07-12 03:10:30 +00001792 msg = N_("File Name to Prepend to");
David Lawrence Ramsey6aec4b82004-03-15 20:26:30 +00001793 else if (append == 1)
David Lawrence Ramsey576bf332004-07-12 03:10:30 +00001794 msg = N_("File Name to Append to");
David Lawrence Ramsey6aec4b82004-03-15 20:26:30 +00001795 else
David Lawrence Ramsey576bf332004-07-12 03:10:30 +00001796 msg = N_("File Name to Write");
David Lawrence Ramsey6aec4b82004-03-15 20:26:30 +00001797
David Lawrence Ramseyf7b5d932004-07-05 14:27:29 +00001798 /* If we're using restricted mode, the filename isn't blank,
1799 * and we're at the "Write File" prompt, disable tab
1800 * completion. */
David Lawrence Ramsey6420d442004-08-11 05:13:08 +00001801 i = statusq(!ISSET(RESTRICTED) || filename[0] == '\0',
David Lawrence Ramsey045883a2004-10-05 20:11:31 +00001802 writefile_list, ans,
David Lawrence Ramsey6aec4b82004-03-15 20:26:30 +00001803#ifndef NANO_SMALL
David Lawrence Ramsey045883a2004-10-05 20:11:31 +00001804 NULL, "%s%s%s", _(msg), formatstr, backupstr
David Lawrence Ramsey6aec4b82004-03-15 20:26:30 +00001805#else
David Lawrence Ramsey045883a2004-10-05 20:11:31 +00001806 "%s", _(msg)
David Lawrence Ramsey6aec4b82004-03-15 20:26:30 +00001807#endif
1808 );
1809
David Lawrence Ramsey951d7142004-10-04 15:23:47 +00001810 if (i < 0) {
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001811 statusbar(_("Cancelled"));
David Lawrence Ramsey045883a2004-10-05 20:11:31 +00001812 retval = -1;
1813 break;
1814 } else {
1815 ans = mallocstrcpy(ans, answer);
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001816
Rocco Corsiaf5c3022001-01-12 07:51:05 +00001817#ifndef DISABLE_BROWSER
David Lawrence Ramsey045883a2004-10-05 20:11:31 +00001818 if (i == NANO_TOFILES_KEY) {
1819 char *tmp = do_browse_from(answer);
Chris Allegretta6fe61492001-05-21 12:56:25 +00001820
David Lawrence Ramsey045883a2004-10-05 20:11:31 +00001821 currshortcut = writefile_list;
1822
1823 if (tmp == NULL)
1824 continue;
1825 free(answer);
1826 answer = tmp;
1827
1828 /* We have a file now. Get out of the statusbar prompt
1829 * cleanly. */
1830 statusq_abort();
1831 } else
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001832#endif /* !DISABLE_BROWSER */
Chris Allegrettacf287c82002-07-20 13:57:41 +00001833#ifndef NANO_SMALL
David Lawrence Ramsey045883a2004-10-05 20:11:31 +00001834 if (i == TOGGLE_DOS_KEY) {
David Lawrence Ramsey3e0c8a62004-11-04 04:08:18 +00001835 fmt = (fmt == DOS_FILE) ? NIX_FILE : DOS_FILE;
David Lawrence Ramsey045883a2004-10-05 20:11:31 +00001836 continue;
1837 } else if (i == TOGGLE_MAC_KEY) {
David Lawrence Ramsey3e0c8a62004-11-04 04:08:18 +00001838 fmt = (fmt == MAC_FILE) ? NIX_FILE : MAC_FILE;
David Lawrence Ramsey045883a2004-10-05 20:11:31 +00001839 continue;
1840 } else if (i == TOGGLE_BACKUP_KEY) {
1841 TOGGLE(BACKUP_FILE);
1842 continue;
1843 } else
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00001844#endif /* !NANO_SMALL */
David Lawrence Ramsey045883a2004-10-05 20:11:31 +00001845 if (i == NANO_PREPEND_KEY) {
1846 append = (append == 2) ? 0 : 2;
1847 continue;
1848 } else if (i == NANO_APPEND_KEY) {
1849 append = (append == 1) ? 0 : 1;
1850 continue;
1851 }
Chris Allegrettaf4b96012001-01-03 07:11:47 +00001852
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001853#ifdef DEBUG
David Lawrence Ramsey045883a2004-10-05 20:11:31 +00001854 fprintf(stderr, "filename is %s\n", answer);
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001855#endif
Chris Allegretta8a0de3b2000-11-24 20:45:14 +00001856
1857#ifdef NANO_EXTRA
David Lawrence Ramsey045883a2004-10-05 20:11:31 +00001858 if (exiting && !ISSET(TEMP_FILE) &&
1859 strcasecmp(answer, "zzy") == 0 && !did_cred) {
1860 do_credits();
1861 did_cred = TRUE;
1862 retval = -1;
1863 break;
David Lawrence Ramseybc503c82003-11-19 23:59:14 +00001864 }
David Lawrence Ramsey045883a2004-10-05 20:11:31 +00001865#endif
1866 if (append == 0 && strcmp(answer, filename) != 0) {
1867 struct stat st;
1868
1869 if (!stat(answer, &st)) {
1870 i = do_yesno(FALSE, _("File exists, OVERWRITE ? "));
1871 if (i == 0 || i == -1)
1872 continue;
1873 /* If we're using restricted mode, we aren't allowed to
1874 * change the name of a file once it has one because
1875 * that would allow reading from or writing to files not
1876 * specified on the command line. In this case, don't
1877 * bother showing the "Different Name" prompt. */
1878 } else if (!ISSET(RESTRICTED) && filename[0] != '\0'
1879#ifndef NANO_SMALL
1880 && (exiting || !ISSET(MARK_ISSET))
1881#endif
1882 ) {
David Lawrence Ramsey2dbcc802004-11-26 18:10:07 +00001883 i = do_yesno(FALSE,
1884 _("Save file under DIFFERENT NAME ? "));
David Lawrence Ramsey045883a2004-10-05 20:11:31 +00001885 if (i == 0 || i == -1)
1886 continue;
1887 }
1888 }
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001889
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +00001890#ifndef NANO_SMALL
David Lawrence Ramsey045883a2004-10-05 20:11:31 +00001891 /* Here's where we allow the selected text to be written to
1892 * a separate file. If we're using restricted mode, this is
1893 * disabled since it allows reading from or writing to files
1894 * not specified on the command line. */
1895 if (!ISSET(RESTRICTED) && !exiting && ISSET(MARK_ISSET))
1896 retval = write_marked(answer, FALSE, append);
1897 else
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +00001898#endif /* !NANO_SMALL */
David Lawrence Ramsey045883a2004-10-05 20:11:31 +00001899 retval = write_file(answer, FALSE, append, FALSE);
Chris Allegretta2d7893d2001-07-11 02:08:33 +00001900
Chris Allegretta355fbe52001-07-14 19:32:47 +00001901#ifdef ENABLE_MULTIBUFFER
David Lawrence Ramsey045883a2004-10-05 20:11:31 +00001902 /* If we're not about to exit, update the current entry in
1903 * the open_files structure. */
1904 if (!exiting)
1905 add_open_file(TRUE);
Chris Allegretta2d7893d2001-07-11 02:08:33 +00001906#endif
David Lawrence Ramsey045883a2004-10-05 20:11:31 +00001907
1908 break;
1909 }
David Lawrence Ramsey6aec4b82004-03-15 20:26:30 +00001910 } /* while (TRUE) */
David Lawrence Ramsey045883a2004-10-05 20:11:31 +00001911
1912 free(ans);
1913 return retval;
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001914}
1915
David Lawrence Ramsey72e51ab2004-07-02 14:31:03 +00001916void do_writeout_void(void)
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001917{
David Lawrence Ramsey72e51ab2004-07-02 14:31:03 +00001918 do_writeout(FALSE);
David Lawrence Ramsey045883a2004-10-05 20:11:31 +00001919 display_main_list();
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001920}
Chris Allegretta04d848e2000-11-05 17:54:41 +00001921
Chris Allegrettabe77c612000-11-24 14:00:16 +00001922/* Return a malloc()ed string containing the actual directory, used
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001923 * to convert ~user and ~/ notation... */
1924char *real_dir_from_tilde(const char *buf)
Chris Allegrettabe77c612000-11-24 14:00:16 +00001925{
Chris Allegrettad865da12002-07-29 23:46:38 +00001926 char *dirtmp = NULL;
Chris Allegrettae5e4d492001-01-23 03:09:15 +00001927
Chris Allegrettabe77c612000-11-24 14:00:16 +00001928 if (buf[0] == '~') {
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001929 size_t i;
1930 const struct passwd *userdata;
1931
1932 /* Figure how how much of the str we need to compare */
1933 for (i = 1; buf[i] != '/' && buf[i] != '\0'; i++)
1934 ;
1935
Chris Allegrettad8451932003-03-11 03:50:40 +00001936 /* Determine home directory using getpwuid() or getpwent(),
1937 don't rely on $HOME */
Chris Allegretta1debce22003-02-13 22:00:19 +00001938 if (i == 1)
1939 userdata = getpwuid(geteuid());
1940 else {
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001941 do {
1942 userdata = getpwent();
1943 } while (userdata != NULL &&
David Lawrence Ramsey2dbcc802004-11-26 18:10:07 +00001944 strncmp(userdata->pw_name, buf + 1, i - 1) != 0);
Chris Allegrettad865da12002-07-29 23:46:38 +00001945 }
1946 endpwent();
Chris Allegretta04fec912000-11-25 04:43:43 +00001947
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00001948 if (userdata != NULL) { /* User found */
David Lawrence Ramsey2dbcc802004-11-26 18:10:07 +00001949 dirtmp = charalloc(strlen(userdata->pw_dir) +
1950 strlen(buf + i) + 1);
Chris Allegrettad865da12002-07-29 23:46:38 +00001951 sprintf(dirtmp, "%s%s", userdata->pw_dir, &buf[i]);
Chris Allegrettabe77c612000-11-24 14:00:16 +00001952 }
Chris Allegrettae5e4d492001-01-23 03:09:15 +00001953 }
Chris Allegrettabe77c612000-11-24 14:00:16 +00001954
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001955 if (dirtmp == NULL)
1956 dirtmp = mallocstrcpy(dirtmp, buf);
1957
Chris Allegretta1bd0ce22000-12-06 05:56:08 +00001958 return dirtmp;
Chris Allegrettabe77c612000-11-24 14:00:16 +00001959}
1960
Chris Allegretta7662c862003-01-13 01:35:15 +00001961#ifndef DISABLE_TABCOMP
David Lawrence Ramsey2dbcc802004-11-26 18:10:07 +00001962/* Tack a slash onto the string we're completing if it's a directory.
1963 * We assume there is room for one more character on the end of buf.
1964 * The return value says whether buf is a directory. */
David Lawrence Ramsey4d44d2d2004-08-01 22:35:31 +00001965int append_slash_if_dir(char *buf, bool *lastwastab, int *place)
Chris Allegrettabe77c612000-11-24 14:00:16 +00001966{
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001967 char *dirptr = real_dir_from_tilde(buf);
Chris Allegrettabe77c612000-11-24 14:00:16 +00001968 struct stat fileinfo;
Chris Allegretta04fec912000-11-25 04:43:43 +00001969 int ret = 0;
Chris Allegrettabe77c612000-11-24 14:00:16 +00001970
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001971 assert(dirptr != buf);
Chris Allegrettabe77c612000-11-24 14:00:16 +00001972
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001973 if (stat(dirptr, &fileinfo) != -1 && S_ISDIR(fileinfo.st_mode)) {
Chris Allegrettabe77c612000-11-24 14:00:16 +00001974 strncat(buf, "/", 1);
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001975 (*place)++;
Chris Allegrettabe77c612000-11-24 14:00:16 +00001976 /* now we start over again with # of tabs so far */
David Lawrence Ramsey4d44d2d2004-08-01 22:35:31 +00001977 *lastwastab = FALSE;
Chris Allegretta04fec912000-11-25 04:43:43 +00001978 ret = 1;
Chris Allegrettabe77c612000-11-24 14:00:16 +00001979 }
1980
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001981 free(dirptr);
Chris Allegretta04fec912000-11-25 04:43:43 +00001982 return ret;
Chris Allegrettabe77c612000-11-24 14:00:16 +00001983}
Chris Allegretta04d848e2000-11-05 17:54:41 +00001984
1985/*
Chris Allegretta7662c862003-01-13 01:35:15 +00001986 * These functions (username_tab_completion(), cwd_tab_completion(), and
1987 * input_tab()) were taken from busybox 0.46 (cmdedit.c). Here is the
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00001988 * notice from that file:
Chris Allegretta04d848e2000-11-05 17:54:41 +00001989 *
1990 * Termios command line History and Editting, originally
1991 * intended for NetBSD sh (ash)
1992 * Copyright (c) 1999
1993 * Main code: Adam Rogoyski <rogoyski@cs.utexas.edu>
1994 * Etc: Dave Cinege <dcinege@psychosis.com>
1995 * Majorly adjusted/re-written for busybox:
1996 * Erik Andersen <andersee@debian.org>
1997 *
1998 * You may use this code as you wish, so long as the original author(s)
1999 * are attributed in any redistributions of the source code.
2000 * This code is 'as is' with no warranty.
2001 * This code may safely be consumed by a BSD or GPL license.
2002 */
2003
2004char **username_tab_completion(char *buf, int *num_matches)
2005{
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00002006 char **matches = (char **)NULL;
Chris Allegretta8d9b11a2001-01-19 04:17:24 +00002007 char *matchline = NULL;
Chris Allegretta2c2c5f22001-01-23 03:27:31 +00002008 struct passwd *userdata;
Chris Allegrettabe77c612000-11-24 14:00:16 +00002009
Chris Allegretta8d9b11a2001-01-19 04:17:24 +00002010 *num_matches = 0;
David Lawrence Ramsey70047ee2003-06-14 20:41:34 +00002011 matches = (char **)nmalloc(BUFSIZ * sizeof(char *));
Chris Allegretta8d9b11a2001-01-19 04:17:24 +00002012
Chris Allegrettabe77c612000-11-24 14:00:16 +00002013 strcat(buf, "*");
Chris Allegretta8d9b11a2001-01-19 04:17:24 +00002014
Chris Allegretta2c2c5f22001-01-23 03:27:31 +00002015 while ((userdata = getpwent()) != NULL) {
Chris Allegrettabe77c612000-11-24 14:00:16 +00002016
David Lawrence Ramsey4d44d2d2004-08-01 22:35:31 +00002017 if (check_wildcard_match(userdata->pw_name, &buf[1])) {
Chris Allegrettabe77c612000-11-24 14:00:16 +00002018
Chris Allegrettabe77c612000-11-24 14:00:16 +00002019 /* Cool, found a match. Add it to the list
2020 * This makes a lot more sense to me (Chris) this way...
2021 */
Chris Allegretta8d9b11a2001-01-19 04:17:24 +00002022
Chris Allegrettae1f14522001-09-19 03:19:43 +00002023#ifndef DISABLE_OPERATINGDIR
2024 /* ...unless the match exists outside the operating
2025 directory, in which case just go to the next match */
2026
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00002027 if (operating_dir != NULL) {
David Lawrence Ramsey72e51ab2004-07-02 14:31:03 +00002028 if (check_operating_dir(userdata->pw_dir, TRUE) != 0)
Chris Allegrettae1f14522001-09-19 03:19:43 +00002029 continue;
2030 }
2031#endif
2032
Chris Allegretta88b09152001-05-17 11:35:43 +00002033 matchline = charalloc(strlen(userdata->pw_name) + 2);
Chris Allegretta2c2c5f22001-01-23 03:27:31 +00002034 sprintf(matchline, "~%s", userdata->pw_name);
2035 matches[*num_matches] = matchline;
David Lawrence Ramseyfd3039a2004-07-17 19:49:12 +00002036 ++(*num_matches);
Chris Allegrettabe77c612000-11-24 14:00:16 +00002037
Chris Allegretta2c2c5f22001-01-23 03:27:31 +00002038 /* If there's no more room, bail out */
2039 if (*num_matches == BUFSIZ)
2040 break;
Chris Allegrettabe77c612000-11-24 14:00:16 +00002041 }
Chris Allegretta2c2c5f22001-01-23 03:27:31 +00002042 }
2043 endpwent();
Chris Allegretta8d9b11a2001-01-19 04:17:24 +00002044
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00002045 return matches;
Chris Allegretta04d848e2000-11-05 17:54:41 +00002046}
2047
2048/* This was originally called exe_n_cwd_tab_completion, but we're not
2049 worried about executables, only filenames :> */
2050
2051char **cwd_tab_completion(char *buf, int *num_matches)
2052{
Chris Allegrettacf287c82002-07-20 13:57:41 +00002053 char *dirname, *dirtmp = NULL, *tmp = NULL, *tmp2 = NULL;
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00002054 char **matches = (char **)NULL;
Chris Allegretta04d848e2000-11-05 17:54:41 +00002055 DIR *dir;
2056 struct dirent *next;
2057
David Lawrence Ramsey70047ee2003-06-14 20:41:34 +00002058 matches = (char **)nmalloc(BUFSIZ * sizeof(char *));
Chris Allegretta04d848e2000-11-05 17:54:41 +00002059
2060 /* Stick a wildcard onto the buf, for later use */
2061 strcat(buf, "*");
2062
Chris Allegretta2c975222000-11-15 01:25:42 +00002063 /* Okie, if there's a / in the buffer, strip out the directory part */
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00002064 if (buf[0] != '\0' && strstr(buf, "/") != NULL) {
Chris Allegrettacf287c82002-07-20 13:57:41 +00002065 dirname = charalloc(strlen(buf) + 1);
Chris Allegretta04d848e2000-11-05 17:54:41 +00002066 tmp = buf + strlen(buf);
2067 while (*tmp != '/' && tmp != buf)
Chris Allegrettab5b89ae2000-11-14 18:25:26 +00002068 tmp--;
Chris Allegretta442f2c52000-11-14 17:46:06 +00002069
Chris Allegretta04d848e2000-11-05 17:54:41 +00002070 tmp++;
2071
Chris Allegrettacf287c82002-07-20 13:57:41 +00002072 strncpy(dirname, buf, tmp - buf + 1);
2073 dirname[tmp - buf] = '\0';
Chris Allegretta442f2c52000-11-14 17:46:06 +00002074
Chris Allegretta04d848e2000-11-05 17:54:41 +00002075 } else {
Chris Allegretta90d30742001-04-03 01:02:38 +00002076
Chris Allegrettacf287c82002-07-20 13:57:41 +00002077 if ((dirname = getcwd(NULL, PATH_MAX + 1)) == NULL)
Chris Allegretta04d848e2000-11-05 17:54:41 +00002078 return matches;
2079 else
2080 tmp = buf;
2081 }
2082
2083#ifdef DEBUG
Chris Allegrettacf287c82002-07-20 13:57:41 +00002084 fprintf(stderr, "\nDir = %s\n", dirname);
Chris Allegretta04d848e2000-11-05 17:54:41 +00002085 fprintf(stderr, "\nbuf = %s\n", buf);
2086 fprintf(stderr, "\ntmp = %s\n", tmp);
2087#endif
Chris Allegrettab5b89ae2000-11-14 18:25:26 +00002088
Chris Allegrettacf287c82002-07-20 13:57:41 +00002089 dirtmp = real_dir_from_tilde(dirname);
2090 free(dirname);
2091 dirname = dirtmp;
Chris Allegrettabe77c612000-11-24 14:00:16 +00002092
2093#ifdef DEBUG
Chris Allegrettacf287c82002-07-20 13:57:41 +00002094 fprintf(stderr, "\nDir = %s\n", dirname);
Chris Allegrettabe77c612000-11-24 14:00:16 +00002095 fprintf(stderr, "\nbuf = %s\n", buf);
2096 fprintf(stderr, "\ntmp = %s\n", tmp);
2097#endif
2098
2099
Chris Allegrettacf287c82002-07-20 13:57:41 +00002100 dir = opendir(dirname);
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00002101 if (dir == NULL) {
Chris Allegretta04d848e2000-11-05 17:54:41 +00002102 /* Don't print an error, just shut up and return */
2103 *num_matches = 0;
2104 beep();
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00002105 return matches;
Chris Allegretta04d848e2000-11-05 17:54:41 +00002106 }
2107 while ((next = readdir(dir)) != NULL) {
2108
Chris Allegretta04d848e2000-11-05 17:54:41 +00002109#ifdef DEBUG
Chris Allegretta2c975222000-11-15 01:25:42 +00002110 fprintf(stderr, "Comparing \'%s\'\n", next->d_name);
Chris Allegretta04d848e2000-11-05 17:54:41 +00002111#endif
2112 /* See if this matches */
David Lawrence Ramsey4d44d2d2004-08-01 22:35:31 +00002113 if (check_wildcard_match(next->d_name, tmp)) {
Chris Allegretta7d97ce72000-11-06 04:04:15 +00002114
2115 /* Cool, found a match. Add it to the list
2116 * This makes a lot more sense to me (Chris) this way...
2117 */
Chris Allegrettae1f14522001-09-19 03:19:43 +00002118
2119#ifndef DISABLE_OPERATINGDIR
2120 /* ...unless the match exists outside the operating
2121 directory, in which case just go to the next match; to
2122 properly do operating directory checking, we have to add the
2123 directory name to the beginning of the proposed match
2124 before we check it */
2125
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00002126 if (operating_dir != NULL) {
Chris Allegrettacf287c82002-07-20 13:57:41 +00002127 tmp2 = charalloc(strlen(dirname) + strlen(next->d_name) + 2);
2128 strcpy(tmp2, dirname);
Chris Allegrettae1f14522001-09-19 03:19:43 +00002129 strcat(tmp2, "/");
2130 strcat(tmp2, next->d_name);
David Lawrence Ramsey72e51ab2004-07-02 14:31:03 +00002131 if (check_operating_dir(tmp2, TRUE) != 0) {
Chris Allegrettae1f14522001-09-19 03:19:43 +00002132 free(tmp2);
2133 continue;
2134 }
2135 free(tmp2);
2136 }
2137#endif
2138
Chris Allegretta7d97ce72000-11-06 04:04:15 +00002139 tmp2 = NULL;
Chris Allegretta88b09152001-05-17 11:35:43 +00002140 tmp2 = charalloc(strlen(next->d_name) + 1);
Chris Allegretta7d97ce72000-11-06 04:04:15 +00002141 strcpy(tmp2, next->d_name);
2142 matches[*num_matches] = tmp2;
Chris Allegretta04d848e2000-11-05 17:54:41 +00002143 ++*num_matches;
Chris Allegretta2c975222000-11-15 01:25:42 +00002144
2145 /* If there's no more room, bail out */
2146 if (*num_matches == BUFSIZ)
2147 break;
Chris Allegretta04d848e2000-11-05 17:54:41 +00002148 }
2149 }
Chris Allegretta3cdf6ff2003-02-08 02:02:02 +00002150 closedir(dir);
2151 free(dirname);
Chris Allegretta04d848e2000-11-05 17:54:41 +00002152
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00002153 return matches;
Chris Allegretta04d848e2000-11-05 17:54:41 +00002154}
2155
Chris Allegretta7662c862003-01-13 01:35:15 +00002156/* This function now has an arg which refers to how much the statusbar
2157 * (place) should be advanced, i.e. the new cursor pos. */
David Lawrence Ramsey4d44d2d2004-08-01 22:35:31 +00002158char *input_tab(char *buf, int place, bool *lastwastab, int *newplace,
2159 bool *list)
Chris Allegretta04d848e2000-11-05 17:54:41 +00002160{
2161 /* Do TAB completion */
Chris Allegrettaec58a992000-11-05 21:54:23 +00002162 static int num_matches = 0, match_matches = 0;
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00002163 static char **matches = (char **)NULL;
Chris Allegretta442f2c52000-11-14 17:46:06 +00002164 int pos = place, i = 0, col = 0, editline = 0;
Chris Allegretta04fec912000-11-25 04:43:43 +00002165 int longestname = 0, is_dir = 0;
Chris Allegretta3b0d1442000-11-05 21:56:54 +00002166 char *foo;
Chris Allegretta04d848e2000-11-05 17:54:41 +00002167
David Lawrence Ramsey4d44d2d2004-08-01 22:35:31 +00002168 *list = FALSE;
Chris Allegretta2084acc2001-11-29 03:43:08 +00002169
David Lawrence Ramseyf21cd102002-06-13 00:40:19 +00002170 if (*lastwastab == FALSE) {
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00002171 char *tmp, *copyto, *matchbuf;
Chris Allegretta04d848e2000-11-05 17:54:41 +00002172
David Lawrence Ramsey4d44d2d2004-08-01 22:35:31 +00002173 *lastwastab = TRUE;
Chris Allegrettab5b89ae2000-11-14 18:25:26 +00002174
Chris Allegretta04d848e2000-11-05 17:54:41 +00002175 /* Make a local copy of the string -- up to the position of the
2176 cursor */
David Lawrence Ramsey70047ee2003-06-14 20:41:34 +00002177 matchbuf = charalloc(strlen(buf) + 2);
2178 memset(matchbuf, '\0', strlen(buf) + 2);
Chris Allegrettae118acc2000-11-06 05:40:03 +00002179
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00002180 strncpy(matchbuf, buf, place);
2181 tmp = matchbuf;
Chris Allegretta04d848e2000-11-05 17:54:41 +00002182
2183 /* skip any leading white space */
David Lawrence Ramseyb54155c2005-01-12 03:25:57 +00002184 while (*tmp && is_blank_char(*tmp))
Chris Allegretta04d848e2000-11-05 17:54:41 +00002185 ++tmp;
2186
2187 /* Free up any memory already allocated */
2188 if (matches != NULL) {
Chris Allegretta442f2c52000-11-14 17:46:06 +00002189 for (i = i; i < num_matches; i++)
2190 free(matches[i]);
Chris Allegretta04d848e2000-11-05 17:54:41 +00002191 free(matches);
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00002192 matches = (char **)NULL;
Chris Allegretta7d97ce72000-11-06 04:04:15 +00002193 num_matches = 0;
Chris Allegretta04d848e2000-11-05 17:54:41 +00002194 }
2195
2196 /* If the word starts with `~' and there is no slash in the word,
2197 * then try completing this word as a username. */
2198
Chris Allegrettad865da12002-07-29 23:46:38 +00002199 /* If the original string begins with a tilde, and the part
2200 we're trying to tab-complete doesn't contain a slash, copy
2201 the part we're tab-completing into buf, so tab completion
2202 will result in buf's containing only the tab-completed
2203 username. */
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00002204 if (buf[0] == '~' && strchr(tmp, '/') == NULL) {
Chris Allegrettad865da12002-07-29 23:46:38 +00002205 buf = mallocstrcpy(buf, tmp);
Chris Allegretta1bd0ce22000-12-06 05:56:08 +00002206 matches = username_tab_completion(tmp, &num_matches);
Chris Allegrettad865da12002-07-29 23:46:38 +00002207 }
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00002208 /* If we're in the middle of the original line, copy the string
2209 only up to the cursor position into buf, so tab completion
2210 will result in buf's containing only the tab-completed
2211 path/filename. */
2212 else if (strlen(buf) > strlen(tmp))
2213 buf = mallocstrcpy(buf, tmp);
Chris Allegretta04d848e2000-11-05 17:54:41 +00002214
2215 /* Try to match everything in the current working directory that
2216 * matches. */
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00002217 if (matches == NULL)
Chris Allegretta04d848e2000-11-05 17:54:41 +00002218 matches = cwd_tab_completion(tmp, &num_matches);
2219
2220 /* Don't leak memory */
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00002221 free(matchbuf);
Chris Allegretta04d848e2000-11-05 17:54:41 +00002222
Chris Allegretta442f2c52000-11-14 17:46:06 +00002223#ifdef DEBUG
2224 fprintf(stderr, "%d matches found...\n", num_matches);
2225#endif
Chris Allegretta04d848e2000-11-05 17:54:41 +00002226 /* Did we find exactly one match? */
Chris Allegrettab5b89ae2000-11-14 18:25:26 +00002227 switch (num_matches) {
Chris Allegrettae118acc2000-11-06 05:40:03 +00002228 case 0:
Chris Allegretta24342432000-11-06 05:41:20 +00002229 blank_edit();
Chris Allegretta2c975222000-11-15 01:25:42 +00002230 wrefresh(edit);
Chris Allegrettae118acc2000-11-06 05:40:03 +00002231 break;
2232 case 1:
Chris Allegretta442f2c52000-11-14 17:46:06 +00002233
David Lawrence Ramsey70047ee2003-06-14 20:41:34 +00002234 buf = charealloc(buf, strlen(buf) + strlen(matches[0]) + 1);
Chris Allegretta442f2c52000-11-14 17:46:06 +00002235
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00002236 if (buf[0] != '\0' && strstr(buf, "/") != NULL) {
Chris Allegrettab5b89ae2000-11-14 18:25:26 +00002237 for (tmp = buf + strlen(buf); *tmp != '/' && tmp != buf;
2238 tmp--);
Chris Allegretta442f2c52000-11-14 17:46:06 +00002239 tmp++;
Chris Allegrettab5b89ae2000-11-14 18:25:26 +00002240 } else
Chris Allegretta442f2c52000-11-14 17:46:06 +00002241 tmp = buf;
2242
David Lawrence Ramseyb8c479a2004-07-31 14:10:23 +00002243 if (strcmp(tmp, matches[0]) == 0)
David Lawrence Ramseyf21cd102002-06-13 00:40:19 +00002244 is_dir = append_slash_if_dir(buf, lastwastab, newplace);
Chris Allegretta04fec912000-11-25 04:43:43 +00002245
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00002246 if (is_dir != 0)
Chris Allegretta04fec912000-11-25 04:43:43 +00002247 break;
Chris Allegretta442f2c52000-11-14 17:46:06 +00002248
2249 copyto = tmp;
Chris Allegrettab5b89ae2000-11-14 18:25:26 +00002250 for (pos = 0; *tmp == matches[0][pos] &&
2251 pos <= strlen(matches[0]); pos++)
Chris Allegretta442f2c52000-11-14 17:46:06 +00002252 tmp++;
2253
Chris Allegretta2c975222000-11-15 01:25:42 +00002254 /* write out the matched name */
Chris Allegrettab5b89ae2000-11-14 18:25:26 +00002255 strncpy(copyto, matches[0], strlen(matches[0]) + 1);
Chris Allegretta442f2c52000-11-14 17:46:06 +00002256 *newplace += strlen(matches[0]) - pos;
2257
Chris Allegrettae1f14522001-09-19 03:19:43 +00002258 /* if an exact match is typed in and Tab is pressed,
2259 *newplace will now be negative; in that case, make it
2260 zero, so that the cursor will stay where it is instead of
2261 moving backward */
2262 if (*newplace < 0)
2263 *newplace = 0;
2264
Chris Allegrettabe77c612000-11-24 14:00:16 +00002265 /* Is it a directory? */
David Lawrence Ramseyf21cd102002-06-13 00:40:19 +00002266 append_slash_if_dir(buf, lastwastab, newplace);
Chris Allegrettabe77c612000-11-24 14:00:16 +00002267
Chris Allegrettae118acc2000-11-06 05:40:03 +00002268 break;
2269 default:
Chris Allegretta88520c92001-05-05 17:45:54 +00002270 /* Check to see if all matches share a beginning, and, if so,
Chris Allegrettab5b89ae2000-11-14 18:25:26 +00002271 tack it onto buf and then beep */
Chris Allegrettaec58a992000-11-05 21:54:23 +00002272
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00002273 if (buf[0] != '\0' && strstr(buf, "/") != NULL) {
Chris Allegrettab5b89ae2000-11-14 18:25:26 +00002274 for (tmp = buf + strlen(buf); *tmp != '/' && tmp != buf;
2275 tmp--);
Chris Allegretta442f2c52000-11-14 17:46:06 +00002276 tmp++;
Chris Allegrettab5b89ae2000-11-14 18:25:26 +00002277 } else
Chris Allegretta442f2c52000-11-14 17:46:06 +00002278 tmp = buf;
2279
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00002280 for (pos = 0; *tmp == matches[0][pos] && *tmp != '\0' &&
Chris Allegrettab5b89ae2000-11-14 18:25:26 +00002281 pos <= strlen(matches[0]); pos++)
Chris Allegretta442f2c52000-11-14 17:46:06 +00002282 tmp++;
2283
David Lawrence Ramsey6aec4b82004-03-15 20:26:30 +00002284 while (TRUE) {
Chris Allegrettaec58a992000-11-05 21:54:23 +00002285 match_matches = 0;
2286
2287 for (i = 0; i < num_matches; i++) {
2288 if (matches[i][pos] == 0)
2289 break;
2290 else if (matches[i][pos] == matches[0][pos])
2291 match_matches++;
2292 }
Chris Allegrettab5b89ae2000-11-14 18:25:26 +00002293 if (match_matches == num_matches &&
2294 (i == num_matches || matches[i] != 0)) {
Chris Allegrettaec58a992000-11-05 21:54:23 +00002295 /* All the matches have the same character at pos+1,
Chris Allegrettab5b89ae2000-11-14 18:25:26 +00002296 so paste it into buf... */
David Lawrence Ramsey70047ee2003-06-14 20:41:34 +00002297 buf = charealloc(buf, strlen(buf) + 2);
Chris Allegretta442f2c52000-11-14 17:46:06 +00002298 strncat(buf, matches[0] + pos, 1);
Chris Allegrettab5b89ae2000-11-14 18:25:26 +00002299 *newplace += 1;
Chris Allegrettaec58a992000-11-05 21:54:23 +00002300 pos++;
Chris Allegretta75864952000-11-05 22:48:35 +00002301 } else {
Chris Allegrettaec58a992000-11-05 21:54:23 +00002302 beep();
2303 break;
2304 }
2305 }
2306 }
Chris Allegretta04d848e2000-11-05 17:54:41 +00002307 } else {
2308 /* Ok -- the last char was a TAB. Since they
2309 * just hit TAB again, print a list of all the
2310 * available choices... */
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00002311 if (matches != NULL && num_matches > 1) {
Chris Allegretta04d848e2000-11-05 17:54:41 +00002312
2313 /* Blank the edit window, and print the matches out there */
2314 blank_edit();
2315 wmove(edit, 0, 0);
2316
Chris Allegrettaec58a992000-11-05 21:54:23 +00002317 editline = 0;
Chris Allegretta2c975222000-11-15 01:25:42 +00002318
Chris Allegrettaec58a992000-11-05 21:54:23 +00002319 /* Figure out the length of the longest filename */
2320 for (i = 0; i < num_matches; i++)
2321 if (strlen(matches[i]) > longestname)
2322 longestname = strlen(matches[i]);
2323
2324 if (longestname > COLS - 1)
2325 longestname = COLS - 1;
2326
Chris Allegretta88b09152001-05-17 11:35:43 +00002327 foo = charalloc(longestname + 5);
Chris Allegretta3b0d1442000-11-05 21:56:54 +00002328
Chris Allegretta04d848e2000-11-05 17:54:41 +00002329 /* Print the list of matches */
2330 for (i = 0, col = 0; i < num_matches; i++) {
Chris Allegrettab5b89ae2000-11-14 18:25:26 +00002331
David Lawrence Ramsey9df069c2004-11-16 02:20:48 +00002332 /* make each filename shown be the same length as the
2333 longest filename, with two spaces at the end */
David Lawrence Ramsey8091d332004-11-16 02:23:10 +00002334 snprintf(foo, longestname + 1, "%s", matches[i]);
Chris Allegrettaec58a992000-11-05 21:54:23 +00002335 while (strlen(foo) < longestname)
2336 strcat(foo, " ");
2337
2338 strcat(foo, " ");
2339
Chris Allegretta442f2c52000-11-14 17:46:06 +00002340 /* Disable el cursor */
2341 curs_set(0);
Chris Allegretta75864952000-11-05 22:48:35 +00002342 /* now, put the match on the screen */
2343 waddnstr(edit, foo, strlen(foo));
2344 col += strlen(foo);
2345
2346 /* And if the next match isn't going to fit on the
2347 line, move to the next one */
Chris Allegretta3cdf6ff2003-02-08 02:02:02 +00002348 if (col > COLS - longestname && i + 1 < num_matches) {
Chris Allegrettaec58a992000-11-05 21:54:23 +00002349 editline++;
2350 wmove(edit, editline, 0);
Chris Allegrettab5b89ae2000-11-14 18:25:26 +00002351 if (editline == editwinrows - 1) {
2352 waddstr(edit, _("(more)"));
2353 break;
2354 }
Chris Allegretta04d848e2000-11-05 17:54:41 +00002355 col = 0;
2356 }
2357 }
Chris Allegretta3b0d1442000-11-05 21:56:54 +00002358 free(foo);
Chris Allegretta04d848e2000-11-05 17:54:41 +00002359 wrefresh(edit);
David Lawrence Ramsey4d44d2d2004-08-01 22:35:31 +00002360 *list = TRUE;
Chris Allegretta24dd8d62000-11-06 05:45:48 +00002361 } else
2362 beep();
Chris Allegretta04d848e2000-11-05 17:54:41 +00002363 }
2364
Chris Allegretta2084acc2001-11-29 03:43:08 +00002365 /* Only refresh the edit window if we don't have a list of filename
2366 matches on it */
David Lawrence Ramsey4d44d2d2004-08-01 22:35:31 +00002367 if (*list == FALSE)
Chris Allegretta2084acc2001-11-29 03:43:08 +00002368 edit_refresh();
Chris Allegretta442f2c52000-11-14 17:46:06 +00002369 curs_set(1);
2370 return buf;
Chris Allegretta04d848e2000-11-05 17:54:41 +00002371}
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00002372#endif /* !DISABLE_TABCOMP */
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002373
David Lawrence Ramsey04e42a62004-02-28 16:24:31 +00002374/* Only print the last part of a path; isn't there a shell
2375 * command for this? */
2376const char *tail(const char *foo)
2377{
2378 const char *tmp = foo + strlen(foo);
2379
2380 while (*tmp != '/' && tmp != foo)
2381 tmp--;
2382
2383 if (*tmp == '/')
2384 tmp++;
2385
2386 return tmp;
2387}
David Lawrence Ramsey04e42a62004-02-28 16:24:31 +00002388
Rocco Corsiaf5c3022001-01-12 07:51:05 +00002389#ifndef DISABLE_BROWSER
David Lawrence Ramseyf28f50e2004-01-09 23:04:55 +00002390/* Our sort routine for file listings -- sort directories before
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00002391 * files, and then alphabetically. */
Chris Allegrettacf287c82002-07-20 13:57:41 +00002392int diralphasort(const void *va, const void *vb)
2393{
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00002394 struct stat fileinfo;
2395 const char *a = *(char *const *)va, *b = *(char *const *)vb;
2396 int aisdir = stat(a, &fileinfo) != -1 && S_ISDIR(fileinfo.st_mode);
2397 int bisdir = stat(b, &fileinfo) != -1 && S_ISDIR(fileinfo.st_mode);
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002398
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00002399 if (aisdir != 0 && bisdir == 0)
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00002400 return -1;
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00002401 if (aisdir == 0 && bisdir != 0)
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00002402 return 1;
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002403
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00002404 return strcasecmp(a, b);
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002405}
2406
Chris Allegretta88520c92001-05-05 17:45:54 +00002407/* Free our malloc()ed memory */
David Lawrence Ramseyd7fd2002004-05-18 01:20:36 +00002408void free_charptrarray(char **array, size_t len)
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002409{
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00002410 for (; len > 0; len--)
2411 free(array[len - 1]);
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002412 free(array);
2413}
2414
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00002415/* Strip one dir from the end of a string. */
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002416void striponedir(char *foo)
2417{
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00002418 char *tmp;
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002419
Chris Allegrettae9b5c6f2003-02-12 23:49:56 +00002420 assert(foo != NULL);
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002421 /* Don't strip the root dir */
Chris Allegrettae9b5c6f2003-02-12 23:49:56 +00002422 if (*foo == '\0' || strcmp(foo, "/") == 0)
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002423 return;
2424
Chris Allegrettae9b5c6f2003-02-12 23:49:56 +00002425 tmp = foo + strlen(foo) - 1;
2426 assert(tmp >= foo);
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002427 if (*tmp == '/')
Chris Allegrettae9b5c6f2003-02-12 23:49:56 +00002428 *tmp = '\0';
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002429
2430 while (*tmp != '/' && tmp != foo)
2431 tmp--;
2432
2433 if (tmp != foo)
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00002434 *tmp = '\0';
2435 else { /* SPK may need to make a 'default' path here */
2436 if (*tmp != '/')
2437 *tmp = '.';
2438 *(tmp + 1) = '\0';
Chris Allegrettaf5de33a2002-02-27 04:14:16 +00002439 }
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002440}
2441
Chris Allegretta858d9d92003-01-30 00:53:32 +00002442int readable_dir(const char *path)
2443{
2444 DIR *dir = opendir(path);
2445
2446 /* If dir is NULL, don't do closedir(), since that changes errno. */
2447 if (dir != NULL)
2448 closedir(dir);
2449 return dir != NULL;
2450}
2451
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00002452/* Initialize the browser code, including the list of files in *path */
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00002453char **browser_init(const char *path, int *longest, int *numents)
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00002454{
2455 DIR *dir;
2456 struct dirent *next;
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00002457 char **filelist;
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00002458 int i = 0;
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00002459 size_t path_len;
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00002460
2461 dir = opendir(path);
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00002462 if (dir == NULL)
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00002463 return NULL;
2464
2465 *numents = 0;
2466 while ((next = readdir(dir)) != NULL) {
David Lawrence Ramseyb8c479a2004-07-31 14:10:23 +00002467 if (strcmp(next->d_name, ".") == 0)
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00002468 continue;
2469 (*numents)++;
2470 if (strlen(next->d_name) > *longest)
2471 *longest = strlen(next->d_name);
2472 }
2473 rewinddir(dir);
2474 *longest += 10;
2475
David Lawrence Ramsey70047ee2003-06-14 20:41:34 +00002476 filelist = (char **)nmalloc(*numents * sizeof (char *));
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00002477
David Lawrence Ramseyb8c479a2004-07-31 14:10:23 +00002478 if (strcmp(path, "/") == 0)
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00002479 path = "";
2480 path_len = strlen(path);
2481
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00002482 while ((next = readdir(dir)) != NULL) {
David Lawrence Ramseyb8c479a2004-07-31 14:10:23 +00002483 if (strcmp(next->d_name, ".") == 0)
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00002484 continue;
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00002485
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00002486 filelist[i] = charalloc(strlen(next->d_name) + path_len + 2);
2487 sprintf(filelist[i], "%s/%s", path, next->d_name);
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00002488 i++;
2489 }
Chris Allegrettae9b5c6f2003-02-12 23:49:56 +00002490 closedir(dir);
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00002491
2492 if (*longest > COLS - 1)
2493 *longest = COLS - 1;
2494
2495 return filelist;
2496}
2497
Chris Allegretta0e9d3c62001-01-04 04:59:17 +00002498/* Our browser function. inpath is the path to start browsing from */
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00002499char *do_browser(const char *inpath)
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002500{
2501 struct stat st;
2502 char *foo, *retval = NULL;
2503 static char *path = NULL;
David Lawrence Ramseya0b5ba22004-08-25 15:39:10 +00002504 int numents = 0, i = 0, j = 0, longest = 0, abort = 0, col = 0;
2505 int selected = 0, editline = 0, width = 0, filecols = 0, lineno = 0;
2506 int kbinput = ERR;
David Lawrence Ramseyeb16f432004-09-27 01:04:50 +00002507 bool meta_key, func_key;
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00002508 char **filelist = (char **)NULL;
David Lawrence Ramseyf5b256b2003-10-03 20:26:25 +00002509#ifndef DISABLE_MOUSE
Chris Allegretta051fc6e2001-05-05 23:17:36 +00002510 MEVENT mevent;
2511#endif
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002512
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00002513 assert(inpath != NULL);
2514
Chris Allegretta0e9d3c62001-01-04 04:59:17 +00002515 /* If path isn't the same as inpath, we are being passed a new
2516 dir as an arg. We free it here so it will be copied from
2517 inpath below */
David Lawrence Ramseyb8c479a2004-07-31 14:10:23 +00002518 if (path != NULL && strcmp(path, inpath) != 0) {
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002519 free(path);
2520 path = NULL;
2521 }
2522
Chris Allegretta0e9d3c62001-01-04 04:59:17 +00002523 /* if path doesn't exist, make it so */
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002524 if (path == NULL)
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00002525 path = mallocstrcpy(NULL, inpath);
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002526
2527 filelist = browser_init(path, &longest, &numents);
Chris Allegretta88b09152001-05-17 11:35:43 +00002528 foo = charalloc(longest + 8);
Chris Allegretta0e9d3c62001-01-04 04:59:17 +00002529
Chris Allegretta88520c92001-05-05 17:45:54 +00002530 /* Sort the list by directory first, then alphabetically */
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002531 qsort(filelist, numents, sizeof(char *), diralphasort);
2532
2533 titlebar(path);
Chris Allegrettaa8c22572002-02-15 19:17:02 +00002534 bottombars(browser_list);
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002535 curs_set(0);
2536 wmove(edit, 0, 0);
2537 i = 0;
2538 width = 0;
2539 filecols = 0;
Chris Allegretta0e9d3c62001-01-04 04:59:17 +00002540
2541 /* Loop invariant: Microsoft sucks. */
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002542 do {
Chris Allegrettaf80a59c2003-01-30 00:57:33 +00002543 char *new_path;
2544 /* Used by the Go To Directory prompt. */
Rocco Corsi12f294c2001-04-14 06:50:24 +00002545
David Lawrence Ramseye5d8f322004-09-30 22:07:21 +00002546 check_statusblank();
Rocco Corsi12f294c2001-04-14 06:50:24 +00002547
Chris Allegrettab10283c2001-05-07 12:08:37 +00002548 currshortcut = browser_list;
Chris Allegretta6fe61492001-05-21 12:56:25 +00002549
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002550 editline = 0;
2551 col = 0;
Chris Allegrettab0f52822001-01-04 05:20:23 +00002552
Chris Allegretta88520c92001-05-05 17:45:54 +00002553 /* Compute line number we're on now, so we don't divide by zero later */
Chris Allegrettadffa2072002-07-24 01:02:26 +00002554 lineno = selected;
2555 if (width != 0)
2556 lineno /= width;
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002557
2558 switch (kbinput) {
Chris Allegretta6b58acd2001-04-12 03:01:53 +00002559
David Lawrence Ramseyf5b256b2003-10-03 20:26:25 +00002560#ifndef DISABLE_MOUSE
Chris Allegrettadffa2072002-07-24 01:02:26 +00002561 case KEY_MOUSE:
Chris Allegretta051fc6e2001-05-05 23:17:36 +00002562 if (getmouse(&mevent) == ERR)
Chris Allegrettadffa2072002-07-24 01:02:26 +00002563 return retval;
Chris Allegretta051fc6e2001-05-05 23:17:36 +00002564
2565 /* If they clicked in the edit window, they probably clicked
2566 on a file */
Chris Allegrettadffa2072002-07-24 01:02:26 +00002567 if (wenclose(edit, mevent.y, mevent.x)) {
Chris Allegretta051fc6e2001-05-05 23:17:36 +00002568 int selectedbackup = selected;
2569
2570 mevent.y -= 2;
2571
Chris Allegrettadffa2072002-07-24 01:02:26 +00002572 /* Longest is the width of each column. There are two
2573 * spaces between each column. */
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00002574 selected = (lineno / editwinrows) * editwinrows * width
Chris Allegrettadffa2072002-07-24 01:02:26 +00002575 + mevent.y * width + mevent.x / (longest + 2);
2576
2577 /* If they clicked beyond the end of a row, select the
2578 * end of that row. */
2579 if (mevent.x > width * (longest + 2))
2580 selected--;
Chris Allegretta051fc6e2001-05-05 23:17:36 +00002581
2582 /* If we're off the screen, reset to the last item.
2583 If we clicked where we did last time, select this name! */
Chris Allegrettaf3fde7c2001-05-06 03:02:21 +00002584 if (selected > numents - 1)
Chris Allegretta051fc6e2001-05-05 23:17:36 +00002585 selected = numents - 1;
Chris Allegrettadffa2072002-07-24 01:02:26 +00002586 else if (selectedbackup == selected)
David Lawrence Ramsey63e73cb2004-11-28 04:52:57 +00002587 /* Put back the 'select' key */
David Lawrence Ramsey74835712004-12-04 17:41:52 +00002588 unget_kbinput('s', FALSE, FALSE);
David Lawrence Ramseyc59979f2004-10-23 02:47:39 +00002589 } else {
2590 /* Must be clicking a shortcut */
David Lawrence Ramseya0b5ba22004-08-25 15:39:10 +00002591 int mouse_x, mouse_y;
2592 get_mouseinput(&mouse_x, &mouse_y, TRUE);
2593 }
Chris Allegretta051fc6e2001-05-05 23:17:36 +00002594
Chris Allegretta6b58acd2001-04-12 03:01:53 +00002595 break;
2596#endif
David Lawrence Ramseyc2c5a512004-01-23 19:26:17 +00002597 case NANO_PREVLINE_KEY:
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002598 if (selected - width >= 0)
2599 selected -= width;
2600 break;
Chris Allegrettaa2c02e92001-07-02 01:31:44 +00002601 case NANO_BACK_KEY:
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002602 if (selected > 0)
2603 selected--;
2604 break;
David Lawrence Ramseyc2c5a512004-01-23 19:26:17 +00002605 case NANO_NEXTLINE_KEY:
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002606 if (selected + width <= numents - 1)
2607 selected += width;
2608 break;
Chris Allegrettaa2c02e92001-07-02 01:31:44 +00002609 case NANO_FORWARD_KEY:
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002610 if (selected < numents - 1)
2611 selected++;
2612 break;
2613 case NANO_PREVPAGE_KEY:
Chris Allegretta6e827412001-01-04 04:41:08 +00002614 case NANO_PREVPAGE_FKEY:
David Lawrence Ramseyf4276942003-12-24 03:33:09 +00002615 case '-': /* Pico compatibility */
Chris Allegrettadffa2072002-07-24 01:02:26 +00002616 if (selected >= (editwinrows + lineno % editwinrows) * width)
David Lawrence Ramseyc3724882004-05-27 18:39:16 +00002617 selected -= (editwinrows + lineno % editwinrows) * width;
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002618 else
2619 selected = 0;
2620 break;
2621 case NANO_NEXTPAGE_KEY:
Chris Allegretta6e827412001-01-04 04:41:08 +00002622 case NANO_NEXTPAGE_FKEY:
David Lawrence Ramseyf4276942003-12-24 03:33:09 +00002623 case ' ': /* Pico compatibility */
Chris Allegrettadffa2072002-07-24 01:02:26 +00002624 selected += (editwinrows - lineno % editwinrows) * width;
2625 if (selected >= numents)
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002626 selected = numents - 1;
2627 break;
Chris Allegrettab3655b42001-10-22 03:15:31 +00002628 case NANO_HELP_KEY:
2629 case NANO_HELP_FKEY:
David Lawrence Ramseyf4276942003-12-24 03:33:09 +00002630 case '?': /* Pico compatibility */
David Lawrence Ramsey00d77982004-08-07 21:27:37 +00002631#ifndef DISABLE_HELP
David Lawrence Ramseye7638ea2004-06-01 19:49:38 +00002632 do_help();
David Lawrence Ramseyae064bf2004-06-01 20:38:00 +00002633 curs_set(0);
David Lawrence Ramsey00d77982004-08-07 21:27:37 +00002634#else
2635 nano_disabled_msg();
2636#endif
David Lawrence Ramseye7638ea2004-06-01 19:49:38 +00002637 break;
Chris Allegrettaa2c02e92001-07-02 01:31:44 +00002638 case NANO_ENTER_KEY:
David Lawrence Ramseyf4276942003-12-24 03:33:09 +00002639 case 'S': /* Pico compatibility */
2640 case 's':
Chris Allegretta0e9d3c62001-01-04 04:59:17 +00002641 /* You can't cd up from / */
David Lawrence Ramseyb8c479a2004-07-31 14:10:23 +00002642 if (strcmp(filelist[selected], "/..") == 0 &&
2643 strcmp(path, "/") == 0) {
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002644 statusbar(_("Can't move up a directory"));
Chris Allegretta0eab2362003-02-03 03:39:05 +00002645 beep();
Rocco Corsi12f294c2001-04-14 06:50:24 +00002646 break;
2647 }
2648
Chris Allegrettae1f14522001-09-19 03:19:43 +00002649#ifndef DISABLE_OPERATINGDIR
David Lawrence Ramseyf4276942003-12-24 03:33:09 +00002650 /* Note: the selected file can be outside the operating
2651 * directory if it is .. or if it is a symlink to
2652 * directory outside the operating directory. */
David Lawrence Ramsey72e51ab2004-07-02 14:31:03 +00002653 if (check_operating_dir(filelist[selected], FALSE) != 0) {
David Lawrence Ramsey36dd87b2004-07-18 17:43:43 +00002654 statusbar(_("Can't go outside of %s in restricted mode"),
2655 operating_dir);
Chris Allegretta0eab2362003-02-03 03:39:05 +00002656 beep();
2657 break;
Chris Allegrettae1f14522001-09-19 03:19:43 +00002658 }
2659#endif
2660
Chris Allegretta0eab2362003-02-03 03:39:05 +00002661 if (stat(filelist[selected], &st) == -1) {
David Lawrence Ramsey36dd87b2004-07-18 17:43:43 +00002662 statusbar(_("Can't open \"%s\": %s"), filelist[selected],
2663 strerror(errno));
Chris Allegretta0eab2362003-02-03 03:39:05 +00002664 beep();
2665 break;
Chris Allegrettaf5de33a2002-02-27 04:14:16 +00002666 }
2667
Chris Allegretta0eab2362003-02-03 03:39:05 +00002668 if (!S_ISDIR(st.st_mode)) {
2669 retval = mallocstrcpy(retval, filelist[selected]);
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002670 abort = 1;
Chris Allegretta0eab2362003-02-03 03:39:05 +00002671 break;
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002672 }
Chris Allegretta0eab2362003-02-03 03:39:05 +00002673
2674 new_path = mallocstrcpy(NULL, filelist[selected]);
2675
2676 if (strcmp("..", tail(new_path)) == 0) {
2677 /* They want to go up a level, so strip off .. and the
2678 current dir */
2679 striponedir(new_path);
2680 /* SPK for '.' path, get the current path via getcwd */
2681 if (strcmp(new_path, ".") == 0) {
2682 free(new_path);
2683 new_path = getcwd(NULL, PATH_MAX + 1);
2684 }
2685 striponedir(new_path);
2686 }
2687
2688 if (!readable_dir(new_path)) {
2689 /* We can't open this dir for some reason. Complain */
David Lawrence Ramsey36dd87b2004-07-18 17:43:43 +00002690 statusbar(_("Can't open \"%s\": %s"), new_path,
2691 strerror(errno));
Chris Allegretta0eab2362003-02-03 03:39:05 +00002692 free(new_path);
2693 break;
2694 }
2695
2696 free_charptrarray(filelist, numents);
2697 free(foo);
2698 free(path);
2699 path = new_path;
2700 return do_browser(path);
2701
David Lawrence Ramsey6420d442004-08-11 05:13:08 +00002702 /* Go to a specific directory */
David Lawrence Ramseye5d8f322004-09-30 22:07:21 +00002703 case NANO_GOTOLINE_KEY:
2704 case NANO_GOTOLINE_FKEY:
David Lawrence Ramseyf4276942003-12-24 03:33:09 +00002705 case 'G': /* Pico compatibility */
2706 case 'g':
Rocco Corsi12f294c2001-04-14 06:50:24 +00002707 curs_set(1);
David Lawrence Ramsey6aec4b82004-03-15 20:26:30 +00002708 j = statusq(FALSE, gotodir_list, "",
Chris Allegretta7662c862003-01-13 01:35:15 +00002709#ifndef NANO_SMALL
David Lawrence Ramsey6aec4b82004-03-15 20:26:30 +00002710 NULL,
Chris Allegretta7662c862003-01-13 01:35:15 +00002711#endif
David Lawrence Ramsey6420d442004-08-11 05:13:08 +00002712 _("Go To Directory"));
Chris Allegrettaa8c22572002-02-15 19:17:02 +00002713 bottombars(browser_list);
Rocco Corsi12f294c2001-04-14 06:50:24 +00002714 curs_set(0);
2715
2716 if (j < 0) {
David Lawrence Ramsey6420d442004-08-11 05:13:08 +00002717 statusbar(_("Cancelled"));
Rocco Corsi12f294c2001-04-14 06:50:24 +00002718 break;
2719 }
2720
Chris Allegrettaf80a59c2003-01-30 00:57:33 +00002721 new_path = real_dir_from_tilde(answer);
Rocco Corsi12f294c2001-04-14 06:50:24 +00002722
Chris Allegrettaf80a59c2003-01-30 00:57:33 +00002723 if (new_path[0] != '/') {
2724 new_path = charealloc(new_path, strlen(path) + strlen(answer) + 2);
2725 sprintf(new_path, "%s/%s", path, answer);
Rocco Corsi12f294c2001-04-14 06:50:24 +00002726 }
2727
Chris Allegrettaf80a59c2003-01-30 00:57:33 +00002728#ifndef DISABLE_OPERATINGDIR
David Lawrence Ramsey72e51ab2004-07-02 14:31:03 +00002729 if (check_operating_dir(new_path, FALSE) != 0) {
Chris Allegrettaf80a59c2003-01-30 00:57:33 +00002730 statusbar(_("Can't go outside of %s in restricted mode"), operating_dir);
2731 free(new_path);
2732 break;
2733 }
2734#endif
2735
2736 if (!readable_dir(new_path)) {
Rocco Corsi12f294c2001-04-14 06:50:24 +00002737 /* We can't open this dir for some reason. Complain */
2738 statusbar(_("Can't open \"%s\": %s"), answer, strerror(errno));
Chris Allegrettaf80a59c2003-01-30 00:57:33 +00002739 free(new_path);
Rocco Corsi12f294c2001-04-14 06:50:24 +00002740 break;
Chris Allegrettaf80a59c2003-01-30 00:57:33 +00002741 }
Rocco Corsi12f294c2001-04-14 06:50:24 +00002742
2743 /* Start over again with the new path value */
Chris Allegrettafdcb9e92003-02-12 02:52:04 +00002744 free_charptrarray(filelist, numents);
2745 free(foo);
Chris Allegrettaf80a59c2003-01-30 00:57:33 +00002746 free(path);
2747 path = new_path;
Rocco Corsi12f294c2001-04-14 06:50:24 +00002748 return do_browser(path);
2749
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002750 /* Stuff we want to abort the browser */
Chris Allegrettaa2c02e92001-07-02 01:31:44 +00002751 case NANO_CANCEL_KEY:
David Lawrence Ramsey4d7c2602003-08-17 02:48:43 +00002752 case NANO_EXIT_KEY:
Chris Allegretta6e827412001-01-04 04:41:08 +00002753 case NANO_EXIT_FKEY:
David Lawrence Ramseyf4276942003-12-24 03:33:09 +00002754 case 'E': /* Pico compatibility */
2755 case 'e':
Chris Allegrettadffa2072002-07-24 01:02:26 +00002756 abort = 1;
2757 break;
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002758 }
2759 if (abort)
2760 break;
2761
Rocco Corsi12f294c2001-04-14 06:50:24 +00002762 blank_edit();
2763
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00002764 if (width != 0)
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002765 i = width * editwinrows * ((selected / width) / editwinrows);
2766 else
2767 i = 0;
2768
2769 wmove(edit, 0, 0);
2770 for (j = i; j < numents && editline <= editwinrows - 1; j++) {
2771 filecols++;
2772
2773 strncpy(foo, tail(filelist[j]), strlen(tail(filelist[j])) + 1);
2774 while (strlen(foo) < longest)
2775 strcat(foo, " ");
2776 col += strlen(foo);
2777
2778 /* Put file info in the string also */
Chris Allegretta88520c92001-05-05 17:45:54 +00002779 /* We use lstat here to detect links; then, if we find a
2780 symlink, we examine it via stat() to see if it is a
Chris Allegretta0876dc92001-03-28 13:04:08 +00002781 directory or just a file symlink */
2782 lstat(filelist[j], &st);
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002783 if (S_ISDIR(st.st_mode))
2784 strcpy(foo + longest - 5, "(dir)");
2785 else {
Chris Allegretta0876dc92001-03-28 13:04:08 +00002786 if (S_ISLNK(st.st_mode)) {
2787 /* Aha! It's a symlink! Now, is it a dir? If so,
2788 mark it as such */
David Lawrence Ramsey3af54d32004-02-25 04:43:27 +00002789 stat(filelist[j], &st);
Chris Allegretta0876dc92001-03-28 13:04:08 +00002790 if (S_ISDIR(st.st_mode))
2791 strcpy(foo + longest - 5, "(dir)");
2792 else
2793 strcpy(foo + longest - 2, "--");
Chris Allegretta90d30742001-04-03 01:02:38 +00002794 } else if (st.st_size < (1 << 10)) /* less than 1 K */
2795 sprintf(foo + longest - 7, "%4d B",
Chris Allegrettad4615622001-05-23 21:54:47 +00002796 (int) st.st_size);
Chris Allegretta90d30742001-04-03 01:02:38 +00002797 else if (st.st_size >= (1 << 30)) /* at least 1 gig */
2798 sprintf(foo + longest - 7, "%4d GB",
2799 (int) st.st_size >> 30);
2800 else if (st.st_size >= (1 << 20)) /* at least 1 meg */
2801 sprintf(foo + longest - 7, "%4d MB",
2802 (int) st.st_size >> 20);
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00002803 else /* It's more than 1 k and less than a meg */
Chris Allegretta90d30742001-04-03 01:02:38 +00002804 sprintf(foo + longest - 7, "%4d KB",
2805 (int) st.st_size >> 10);
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002806 }
2807
David Lawrence Ramsey0084eaa2002-11-04 16:05:42 +00002808 /* Highlight the currently selected file/dir */
Chris Allegrettadffa2072002-07-24 01:02:26 +00002809 if (j == selected)
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002810 wattron(edit, A_REVERSE);
Chris Allegrettadffa2072002-07-24 01:02:26 +00002811 waddstr(edit, foo);
2812 if (j == selected)
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002813 wattroff(edit, A_REVERSE);
2814
Chris Allegretta0e9d3c62001-01-04 04:59:17 +00002815 /* And add some space between the cols */
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002816 waddstr(edit, " ");
2817 col += 2;
2818
2819 /* And if the next entry isn't going to fit on the
2820 line, move to the next one */
Chris Allegrettadffa2072002-07-24 01:02:26 +00002821 if (col > COLS - longest) {
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002822 editline++;
2823 wmove(edit, editline, 0);
2824 col = 0;
2825 if (width == 0)
2826 width = filecols;
2827 }
2828 }
David Lawrence Ramseya0b5ba22004-08-25 15:39:10 +00002829 wrefresh(edit);
David Lawrence Ramseyeb16f432004-09-27 01:04:50 +00002830 } while ((kbinput = get_kbinput(edit, &meta_key, &func_key)) !=
2831 NANO_EXIT_KEY && kbinput != NANO_EXIT_FKEY);
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002832 curs_set(1);
2833 blank_edit();
Chris Allegretta4dc03d52002-05-11 03:04:44 +00002834 titlebar(NULL);
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002835 edit_refresh();
2836
Chris Allegretta0e9d3c62001-01-04 04:59:17 +00002837 /* cleanup */
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002838 free_charptrarray(filelist, numents);
2839 free(foo);
2840 return retval;
2841}
Chris Allegretta150469a2001-01-05 21:13:14 +00002842
Chris Allegretta88520c92001-05-05 17:45:54 +00002843/* Browser front end, checks to see if inpath has a dir in it and, if so,
Chris Allegretta150469a2001-01-05 21:13:14 +00002844 starts do_browser from there, else from the current dir */
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00002845char *do_browse_from(const char *inpath)
Chris Allegretta150469a2001-01-05 21:13:14 +00002846{
2847 struct stat st;
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00002848 char *bob;
Chris Allegretta858d9d92003-01-30 00:53:32 +00002849 /* The result of do_browser; the selected file name. */
2850 char *path;
2851 /* inpath, tilde expanded. */
Chris Allegretta90d30742001-04-03 01:02:38 +00002852
Chris Allegretta858d9d92003-01-30 00:53:32 +00002853 assert(inpath != NULL);
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00002854
Chris Allegretta858d9d92003-01-30 00:53:32 +00002855 path = real_dir_from_tilde(inpath);
2856
2857 /*
2858 * Perhaps path is a directory. If so, we will pass that to
2859 * do_browser. Otherwise, perhaps path is a directory / a file. So
2860 * we try stripping off the last path element. If it still isn't a
2861 * directory, just use the current directory. */
2862
2863 if (stat(path, &st) == -1 || !S_ISDIR(st.st_mode)) {
2864 striponedir(path);
Chris Allegrettae9b5c6f2003-02-12 23:49:56 +00002865 if (stat(path, &st) == -1 || !S_ISDIR(st.st_mode)) {
2866 free(path);
Chris Allegretta858d9d92003-01-30 00:53:32 +00002867 path = getcwd(NULL, PATH_MAX + 1);
Chris Allegrettae9b5c6f2003-02-12 23:49:56 +00002868 }
Chris Allegretta90d30742001-04-03 01:02:38 +00002869 }
Chris Allegretta150469a2001-01-05 21:13:14 +00002870
Chris Allegretta858d9d92003-01-30 00:53:32 +00002871#ifndef DISABLE_OPERATINGDIR
2872 /* If the resulting path isn't in the operating directory, use that. */
David Lawrence Ramsey72e51ab2004-07-02 14:31:03 +00002873 if (check_operating_dir(path, FALSE) != 0)
Chris Allegretta858d9d92003-01-30 00:53:32 +00002874 path = mallocstrcpy(path, operating_dir);
2875#endif
Chris Allegretta150469a2001-01-05 21:13:14 +00002876
Chris Allegretta858d9d92003-01-30 00:53:32 +00002877 if (!readable_dir(path)) {
2878 beep();
2879 bob = NULL;
2880 } else
2881 bob = do_browser(path);
2882 free(path);
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00002883 return bob;
Chris Allegretta150469a2001-01-05 21:13:14 +00002884}
Chris Allegrettacf287c82002-07-20 13:57:41 +00002885#endif /* !DISABLE_BROWSER */
Chris Allegrettaf3de8b52003-01-16 23:44:46 +00002886
David Lawrence Ramsey6420d442004-08-11 05:13:08 +00002887#if !defined(NANO_SMALL) && defined(ENABLE_NANORC)
David Lawrence Ramseya27bd652004-08-17 05:23:38 +00002888/* Return $HOME/.nano_history, or NULL if we can't find the homedir.
2889 * The string is dynamically allocated, and should be freed. */
2890char *histfilename(void)
2891{
2892 char *nanohist = NULL;
2893
2894 if (homedir != NULL) {
2895 size_t homelen = strlen(homedir);
2896
2897 nanohist = charalloc(homelen + 15);
2898 strcpy(nanohist, homedir);
2899 strcpy(nanohist + homelen, "/.nano_history");
2900 }
2901 return nanohist;
2902}
2903
Chris Allegrettaf3de8b52003-01-16 23:44:46 +00002904void load_history(void)
2905{
David Lawrence Ramseya27bd652004-08-17 05:23:38 +00002906 char *nanohist = histfilename();
Chris Allegrettaf3de8b52003-01-16 23:44:46 +00002907
David Lawrence Ramsey6420d442004-08-11 05:13:08 +00002908 /* assume do_rcfile() has reported missing home dir */
David Lawrence Ramseya27bd652004-08-17 05:23:38 +00002909 if (nanohist != NULL) {
2910 FILE *hist = fopen(nanohist, "r");
Chris Allegrettaf3de8b52003-01-16 23:44:46 +00002911
David Lawrence Ramsey417b03a2003-09-06 21:44:37 +00002912 if (hist == NULL) {
David Lawrence Ramsey6420d442004-08-11 05:13:08 +00002913 if (errno != ENOENT) {
Chris Allegrettad8451932003-03-11 03:50:40 +00002914 /* Don't save history when we quit. */
2915 UNSET(HISTORYLOG);
David Lawrence Ramsey6420d442004-08-11 05:13:08 +00002916 rcfile_error(N_("Error reading %s: %s"), nanohist, strerror(errno));
David Lawrence Ramsey7dfec432004-08-12 15:20:14 +00002917 fprintf(stderr, _("\nPress Return to continue starting nano\n"));
2918 while (getchar() != '\n')
2919 ;
Chris Allegrettad8451932003-03-11 03:50:40 +00002920 }
Chris Allegrettaf3de8b52003-01-16 23:44:46 +00002921 } else {
David Lawrence Ramseya27bd652004-08-17 05:23:38 +00002922 historyheadtype *history = &search_history;
2923 char *line = NULL;
2924 size_t buflen = 0;
2925 ssize_t read;
2926
2927 while ((read = getline(&line, &buflen, hist)) >= 0) {
2928 if (read > 0 && line[read - 1] == '\n') {
2929 read--;
2930 line[read] = '\0';
2931 }
2932 if (read > 0) {
2933 unsunder(line, read);
2934 update_history(history, line);
2935 } else
Chris Allegrettaf3de8b52003-01-16 23:44:46 +00002936 history = &replace_history;
2937 }
2938 fclose(hist);
David Lawrence Ramseya27bd652004-08-17 05:23:38 +00002939 free(line);
Chris Allegrettaf3de8b52003-01-16 23:44:46 +00002940 UNSET(HISTORY_CHANGED);
2941 }
David Lawrence Ramseya27bd652004-08-17 05:23:38 +00002942 free(nanohist);
Chris Allegrettaf3de8b52003-01-16 23:44:46 +00002943 }
2944}
2945
David Lawrence Ramseya27bd652004-08-17 05:23:38 +00002946bool writehist(FILE *hist, historyheadtype *histhead)
2947{
2948 historytype *h;
2949
2950 /* write oldest first */
2951 for (h = histhead->tail; h->prev != NULL; h = h->prev) {
2952 size_t len = strlen(h->data);
2953
2954 sunder(h->data);
2955 if (fwrite(h->data, sizeof(char), len, hist) < len ||
2956 putc('\n', hist) == EOF)
2957 return FALSE;
2958 }
2959 return TRUE;
2960}
2961
Chris Allegrettaf3de8b52003-01-16 23:44:46 +00002962/* save histories to ~/.nano_history */
2963void save_history(void)
2964{
David Lawrence Ramseya27bd652004-08-17 05:23:38 +00002965 char *nanohist;
Chris Allegrettaf3de8b52003-01-16 23:44:46 +00002966
2967 /* don't save unchanged or empty histories */
David Lawrence Ramsey576bf332004-07-12 03:10:30 +00002968 if ((search_history.count == 0 && replace_history.count == 0) ||
David Lawrence Ramsey6420d442004-08-11 05:13:08 +00002969 !ISSET(HISTORY_CHANGED) || ISSET(VIEW_MODE))
Chris Allegrettaf3de8b52003-01-16 23:44:46 +00002970 return;
2971
David Lawrence Ramseya27bd652004-08-17 05:23:38 +00002972 nanohist = histfilename();
Chris Allegretta1debce22003-02-13 22:00:19 +00002973
David Lawrence Ramseya27bd652004-08-17 05:23:38 +00002974 if (nanohist != NULL) {
2975 FILE *hist = fopen(nanohist, "wb");
2976
David Lawrence Ramsey36dd87b2004-07-18 17:43:43 +00002977 if (hist == NULL)
David Lawrence Ramsey6420d442004-08-11 05:13:08 +00002978 rcfile_error(N_("Error writing %s: %s"), nanohist, strerror(errno));
David Lawrence Ramsey36dd87b2004-07-18 17:43:43 +00002979 else {
Chris Allegrettaf3de8b52003-01-16 23:44:46 +00002980 /* set rw only by owner for security ?? */
2981 chmod(nanohist, S_IRUSR | S_IWUSR);
David Lawrence Ramseya27bd652004-08-17 05:23:38 +00002982
2983 if (!writehist(hist, &search_history) ||
2984 putc('\n', hist) == EOF ||
2985 !writehist(hist, &replace_history))
David Lawrence Ramsey1e7f1ea2004-08-12 02:11:35 +00002986 rcfile_error(N_("Error writing %s: %s"), nanohist, strerror(errno));
Chris Allegrettaf3de8b52003-01-16 23:44:46 +00002987 fclose(hist);
2988 }
2989 free(nanohist);
2990 }
2991}
David Lawrence Ramsey6420d442004-08-11 05:13:08 +00002992#endif /* !NANO_SMALL && ENABLE_NANORC */