blob: 682278176c8bf6dc95e30acfecf39edeedb483c4 [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 Ramseydaa533a2005-02-07 05:31:51 +0000299 totsize--;
David Lawrence Ramsey2ab03f62002-10-17 02:19:31 +0000300 }
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000301 }
Chris Allegretta76e291b2001-10-14 19:05:10 +0000302
David Lawrence Ramsey23c44502005-01-27 20:49:07 +0000303 get_totals(fileage, filebot, NULL, &num_chars);
304 totsize += num_chars;
305
Chris Allegretta76e291b2001-10-14 19:05:10 +0000306#ifndef NANO_SMALL
David Lawrence Ramsey3e0c8a62004-11-04 04:08:18 +0000307 if (format == 3)
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000308 statusbar(
309 P_("Read %lu line (Converted from DOS and Mac format)",
310 "Read %lu lines (Converted from DOS and Mac format)",
311 (unsigned long)num_lines), (unsigned long)num_lines);
David Lawrence Ramsey3e0c8a62004-11-04 04:08:18 +0000312 else if (format == 2) {
313 fmt = MAC_FILE;
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000314 statusbar(P_("Read %lu line (Converted from Mac format)",
315 "Read %lu lines (Converted from Mac format)",
316 (unsigned long)num_lines), (unsigned long)num_lines);
David Lawrence Ramsey3e0c8a62004-11-04 04:08:18 +0000317 } else if (format == 1) {
318 fmt = DOS_FILE;
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000319 statusbar(P_("Read %lu line (Converted from DOS format)",
320 "Read %lu lines (Converted from DOS format)",
321 (unsigned long)num_lines), (unsigned long)num_lines);
David Lawrence Ramsey7c1f17a2004-10-03 13:47:26 +0000322 } else
Chris Allegretta76e291b2001-10-14 19:05:10 +0000323#endif
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000324 statusbar(P_("Read %lu line", "Read %lu lines",
David Lawrence Ramsey23c44502005-01-27 20:49:07 +0000325 (unsigned long)num_lines), (unsigned long)num_lines);
David Lawrence Ramsey7bf00de2003-09-23 04:25:05 +0000326
Adam Rogoyski1e328fb2000-07-08 03:57:16 +0000327 totlines += num_lines;
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000328}
329
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000330/* Open the file (and decide if it exists). If newfie is TRUE, display
331 * "New File" if the file is missing. Otherwise, say "[filename] not
332 * found".
333 *
334 * Return -2 if we say "New File". Otherwise, -1 if the file isn't
335 * opened, 0 otherwise. The file might still have an error while
336 * reading with a 0 return value. *f is set to the opened file. */
337int open_file(const char *filename, bool newfie, FILE **f)
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000338{
339 int fd;
340 struct stat fileinfo;
341
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000342 assert(f != NULL);
David Lawrence Ramsey23c44502005-01-27 20:49:07 +0000343
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000344 if (filename == NULL || filename[0] == '\0' ||
345 stat(filename, &fileinfo) == -1) {
346 if (newfie) {
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000347 statusbar(_("New File"));
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000348 return -2;
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000349 }
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000350 statusbar(_("\"%s\" not found"), filename);
351 return -1;
Chris Allegretta54c1f792003-01-26 04:11:09 +0000352 } else if (S_ISDIR(fileinfo.st_mode) || S_ISCHR(fileinfo.st_mode) ||
353 S_ISBLK(fileinfo.st_mode)) {
354 /* Don't open character or block files. Sorry, /dev/sndstat! */
David Lawrence Ramseye08765d2004-11-26 20:17:49 +0000355 statusbar(S_ISDIR(fileinfo.st_mode) ? _("\"%s\" is a directory")
356 : _("File \"%s\" is a device file"), filename);
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000357 return -1;
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000358 } else if ((fd = open(filename, O_RDONLY)) == -1) {
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000359 statusbar(_("Error reading %s: %s"), filename, strerror(errno));
360 return -1;
361 } else {
David Lawrence Ramseye08765d2004-11-26 20:17:49 +0000362 /* File is A-OK. Open it in binary mode for our own end-of-line
363 * character munging. */
364 *f = fdopen(fd, "rb");
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000365
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000366 if (*f == NULL) {
David Lawrence Ramseye08765d2004-11-26 20:17:49 +0000367 statusbar(_("Error reading %s: %s"), filename,
368 strerror(errno));
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000369 close(fd);
370 } else
371 statusbar(_("Reading File"));
372 }
373 return 0;
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000374}
375
Chris Allegretta48b06702002-02-22 04:30:50 +0000376/* This function will return the name of the first available extension
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000377 * of a filename (starting with filename.save, then filename.save.1,
378 * etc.). Memory is allocated for the return value. If no writable
David Lawrence Ramsey049e4a52004-08-10 23:05:59 +0000379 * extension exists, we return "". */
Chris Allegretta7162e3d2002-04-06 05:02:14 +0000380char *get_next_filename(const char *name)
Chris Allegretta48b06702002-02-22 04:30:50 +0000381{
382 int i = 0;
David Lawrence Ramsey049e4a52004-08-10 23:05:59 +0000383 char *buf;
384 size_t namelen = strlen(name);
Chris Allegretta48b06702002-02-22 04:30:50 +0000385
David Lawrence Ramsey049e4a52004-08-10 23:05:59 +0000386 buf = charalloc(namelen + num_of_digits(INT_MAX) + 7);
Chris Allegretta48b06702002-02-22 04:30:50 +0000387 strcpy(buf, name);
David Lawrence Ramsey049e4a52004-08-10 23:05:59 +0000388 strcpy(buf + namelen, ".save");
389 namelen += 5;
Chris Allegretta48b06702002-02-22 04:30:50 +0000390
David Lawrence Ramsey6aec4b82004-03-15 20:26:30 +0000391 while (TRUE) {
David Lawrence Ramsey049e4a52004-08-10 23:05:59 +0000392 struct stat fs;
Chris Allegretta48b06702002-02-22 04:30:50 +0000393
394 if (stat(buf, &fs) == -1)
Chris Allegretta7162e3d2002-04-06 05:02:14 +0000395 return buf;
Chris Allegretta48b06702002-02-22 04:30:50 +0000396 if (i == INT_MAX)
397 break;
398
399 i++;
David Lawrence Ramsey049e4a52004-08-10 23:05:59 +0000400 sprintf(buf + namelen, ".%d", i);
Chris Allegretta48b06702002-02-22 04:30:50 +0000401 }
402
Chris Allegretta7162e3d2002-04-06 05:02:14 +0000403 /* We get here only if there is no possible save file. */
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000404 null_at(&buf, 0);
Chris Allegretta48b06702002-02-22 04:30:50 +0000405 return buf;
406}
407
David Lawrence Ramsey146bb602004-08-27 21:02:38 +0000408#ifndef NANO_SMALL
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000409void execute_command(const char *command)
410{
411#ifdef ENABLE_MULTIBUFFER
412 if (ISSET(MULTIBUFFER)) {
413 /* Update the current entry in the open_files structure. */
414 add_open_file(TRUE);
415 new_file();
416 UNSET(MODIFIED);
417 UNSET(MARK_ISSET);
418 }
419#endif /* ENABLE_MULTIBUFFER */
420 open_pipe(command);
421#ifdef ENABLE_MULTIBUFFER
422 /* Add this new entry to the open_files structure. */
423 if (ISSET(MULTIBUFFER))
424 load_file();
425#endif /* ENABLE_MULTIBUFFER */
426}
427#endif /* !NANO_SMALL */
David Lawrence Ramsey146bb602004-08-27 21:02:38 +0000428
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000429/* name is a file name to open. We make a new buffer if necessary, then
430 * open and read the file. */
431void load_buffer(const char *name)
432{
David Lawrence Ramsey3d002ed2004-10-31 22:45:24 +0000433 bool new_buffer = (fileage == NULL
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000434#ifdef ENABLE_MULTIBUFFER
435 || ISSET(MULTIBUFFER)
Chris Allegretta6fe61492001-05-21 12:56:25 +0000436#endif
David Lawrence Ramsey3d002ed2004-10-31 22:45:24 +0000437 );
David Lawrence Ramsey1c293d22004-11-05 16:22:27 +0000438 /* new_buffer says whether we load into this buffer or a new
439 * one. If new_buffer is TRUE, we display "New File" if the
440 * file is not found, and if it is found we set filename and add
441 * a new open_files entry. */
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000442 FILE *f;
443 int rc;
444 /* rc == -2 means that the statusbar displayed "New File". -1
445 * means that the open failed. 0 means success. */
Chris Allegretta6fe61492001-05-21 12:56:25 +0000446
Chris Allegrettaf5de33a2002-02-27 04:14:16 +0000447#ifndef DISABLE_OPERATINGDIR
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000448 if (check_operating_dir(name, FALSE)) {
David Lawrence Ramsey8a2c0ba2004-11-24 20:36:36 +0000449 statusbar(_("Can't insert file from outside of %s"),
450 operating_dir);
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000451 return;
452 }
Chris Allegretta7662c862003-01-13 01:35:15 +0000453#endif
Chris Allegrettaf7c68112002-09-03 22:58:40 +0000454
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000455#ifdef ENABLE_MULTIBUFFER
456 /* Update the current entry in the open_files structure. */
457 add_open_file(TRUE);
458#endif
459
460 rc = open_file(name, new_buffer, &f);
461
462#ifdef ENABLE_MULTIBUFFER
463 if (rc != -1 && ISSET(MULTIBUFFER)) {
464 UNSET(MODIFIED);
465#ifndef NANO_SMALL
466 UNSET(MARK_ISSET);
467#endif
468 }
469#endif
470
471 if (rc != -1 && new_buffer) {
472 filename = mallocstrcpy(filename, name);
473 new_file();
474 }
475
476 if (rc == 0) {
David Lawrence Ramsey78d644a2004-11-05 16:24:35 +0000477 file_format fmt_save = fmt;
478
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000479 read_file(f, filename);
David Lawrence Ramsey78d644a2004-11-05 16:24:35 +0000480
481 /* If we're not loading into a new buffer, preserve the file
482 * format. */
483 if (!new_buffer)
484 fmt = fmt_save;
485
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000486#ifndef NANO_SMALL
487 stat(filename, &originalfilestat);
488#endif
489 }
490
491 /* Add this new entry to the open_files structure if we have
492 * multibuffer support, or to the main filestruct if we don't. */
493 if (rc != -1 && new_buffer)
494 load_file();
495}
496
David Lawrence Ramseybe908f62004-10-01 18:34:30 +0000497void do_insertfile(
498#ifndef NANO_SMALL
499 bool execute
500#else
501 void
502#endif
503 )
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000504{
505 int i;
506 const char *msg;
David Lawrence Ramsey670a56c2004-09-28 16:03:03 +0000507 char *ans = mallocstrcpy(NULL, "");
David Lawrence Ramsey045883a2004-10-05 20:11:31 +0000508 /* The last answer the user typed on the statusbar. */
David Lawrence Ramseyb73c0c02004-11-05 15:25:53 +0000509 filestruct *edittop_save = edittop;
David Lawrence Ramsey036a3d42004-11-15 21:49:21 +0000510 int current_y_save = current_y;
David Lawrence Ramsey4a3879f2004-11-15 20:42:31 +0000511 bool at_edittop = FALSE;
512 /* Whether we're at the top of the edit window. */
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000513
David Lawrence Ramsey487149e2004-10-05 20:24:48 +0000514#ifndef DISABLE_WRAPPING
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000515 wrap_reset();
David Lawrence Ramsey487149e2004-10-05 20:24:48 +0000516#endif
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000517
David Lawrence Ramsey045883a2004-10-05 20:11:31 +0000518 while (TRUE) {
David Lawrence Ramseye5d8f322004-09-30 22:07:21 +0000519#ifndef NANO_SMALL
David Lawrence Ramsey045883a2004-10-05 20:11:31 +0000520 if (execute) {
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000521#ifdef ENABLE_MULTIBUFFER
David Lawrence Ramsey045883a2004-10-05 20:11:31 +0000522 if (ISSET(MULTIBUFFER))
523 msg = N_("Command to execute in new buffer [from %s] ");
524 else
Chris Allegrettaf5de33a2002-02-27 04:14:16 +0000525#endif
David Lawrence Ramsey045883a2004-10-05 20:11:31 +0000526 msg = N_("Command to execute [from %s] ");
527 } else {
David Lawrence Ramseye5d8f322004-09-30 22:07:21 +0000528#endif
529#ifdef ENABLE_MULTIBUFFER
David Lawrence Ramsey045883a2004-10-05 20:11:31 +0000530 if (ISSET(MULTIBUFFER)) {
531 msg = N_("File to insert into new buffer [from %s] ");
532 } else
David Lawrence Ramseye5d8f322004-09-30 22:07:21 +0000533#endif
David Lawrence Ramsey045883a2004-10-05 20:11:31 +0000534 msg = N_("File to insert [from %s] ");
David Lawrence Ramseye5d8f322004-09-30 22:07:21 +0000535#ifndef NANO_SMALL
536 }
537#endif
David Lawrence Ramsey04a8d1c2004-09-28 22:14:58 +0000538
David Lawrence Ramsey045883a2004-10-05 20:11:31 +0000539 i = statusq(TRUE,
David Lawrence Ramseye5d8f322004-09-30 22:07:21 +0000540#ifndef NANO_SMALL
David Lawrence Ramseybe908f62004-10-01 18:34:30 +0000541 execute ? extcmd_list :
David Lawrence Ramseye5d8f322004-09-30 22:07:21 +0000542#endif
543 insertfile_list, ans,
Chris Allegretta7662c862003-01-13 01:35:15 +0000544#ifndef NANO_SMALL
David Lawrence Ramsey6aec4b82004-03-15 20:26:30 +0000545 NULL,
Chris Allegrettaf7c68112002-09-03 22:58:40 +0000546#endif
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000547 _(msg),
548#ifndef DISABLE_OPERATINGDIR
549 operating_dir != NULL && strcmp(operating_dir, ".") != 0 ?
550 operating_dir :
Chris Allegretta7662c862003-01-13 01:35:15 +0000551#endif
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000552 "./");
Chris Allegrettaf5de33a2002-02-27 04:14:16 +0000553
David Lawrence Ramseyfdd3bec2004-11-07 21:30:55 +0000554 /* If we're in multibuffer mode and the filename or command is
555 * blank, open a new buffer instead of canceling. */
David Lawrence Ramseyc136f752004-11-07 18:35:53 +0000556 if (i == -1 || (i == -2
David Lawrence Ramseya084ad92004-11-07 18:22:33 +0000557#ifdef ENABLE_MULTIBUFFER
David Lawrence Ramseyc136f752004-11-07 18:35:53 +0000558 && !ISSET(MULTIBUFFER)
David Lawrence Ramseya084ad92004-11-07 18:22:33 +0000559#endif
David Lawrence Ramseyc136f752004-11-07 18:35:53 +0000560 ))
David Lawrence Ramseya084ad92004-11-07 18:22:33 +0000561 {
David Lawrence Ramsey045883a2004-10-05 20:11:31 +0000562 statusbar(_("Cancelled"));
563 break;
564 } else {
David Lawrence Ramsey31f99362004-11-05 16:01:04 +0000565 size_t pww_save = placewewant;
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000566
David Lawrence Ramsey045883a2004-10-05 20:11:31 +0000567 ans = mallocstrcpy(ans, answer);
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000568
David Lawrence Ramseye5d8f322004-09-30 22:07:21 +0000569#if !defined(NANO_SMALL) && defined(ENABLE_MULTIBUFFER)
David Lawrence Ramsey045883a2004-10-05 20:11:31 +0000570 if (i == TOGGLE_MULTIBUFFER_KEY) {
571 /* Don't allow toggling if we're in view mode. */
572 if (!ISSET(VIEW_MODE))
573 TOGGLE(MULTIBUFFER);
574 continue;
575 }
David Lawrence Ramsey04a8d1c2004-09-28 22:14:58 +0000576#endif
David Lawrence Ramsey6aec4b82004-03-15 20:26:30 +0000577
Chris Allegretta434d6862003-02-03 04:55:17 +0000578#ifndef DISABLE_BROWSER
David Lawrence Ramsey045883a2004-10-05 20:11:31 +0000579 if (i == NANO_TOFILES_KEY) {
580 char *tmp = do_browse_from(answer);
Chris Allegretta434d6862003-02-03 04:55:17 +0000581
David Lawrence Ramsey045883a2004-10-05 20:11:31 +0000582 if (tmp == NULL)
583 continue;
David Lawrence Ramseyb49daec2004-10-05 02:46:24 +0000584 free(answer);
585 answer = tmp;
David Lawrence Ramsey045883a2004-10-05 20:11:31 +0000586
587 /* We have a file now. Get out of the statusbar prompt
588 * cleanly. */
589 statusq_abort();
David Lawrence Ramseyb49daec2004-10-05 02:46:24 +0000590 }
Chris Allegrettaf4b96012001-01-03 07:11:47 +0000591#endif
592
Chris Allegretta52c5a6e2002-03-21 05:07:28 +0000593#ifndef NANO_SMALL
David Lawrence Ramsey045883a2004-10-05 20:11:31 +0000594 if (i == NANO_TOOTHERINSERT_KEY) {
595 execute = !execute;
596 continue;
597 }
David Lawrence Ramseyc15802f2004-11-07 18:09:41 +0000598#endif
David Lawrence Ramseye5d8f322004-09-30 22:07:21 +0000599
David Lawrence Ramseyc15802f2004-11-07 18:09:41 +0000600#ifdef ENABLE_MULTIBUFFER
601 if (!ISSET(MULTIBUFFER)) {
602#endif
603 /* If we're not inserting into a new buffer, partition
604 * the filestruct so that it contains no text and hence
David Lawrence Ramsey4a3879f2004-11-15 20:42:31 +0000605 * looks like a new buffer, and keep track of whether
606 * the top of the partition is the top of the edit
607 * window. */
David Lawrence Ramseyc15802f2004-11-07 18:09:41 +0000608 filepart = partition_filestruct(current, current_x,
609 current, current_x);
David Lawrence Ramsey4a3879f2004-11-15 20:42:31 +0000610 at_edittop = (fileage == edittop);
David Lawrence Ramseyc15802f2004-11-07 18:09:41 +0000611#ifdef ENABLE_MULTIBUFFER
612 }
613#endif
614
615#ifndef NANO_SMALL
David Lawrence Ramsey045883a2004-10-05 20:11:31 +0000616 if (execute)
617 execute_command(answer);
618 else {
David Lawrence Ramseyd717d4b2004-07-10 16:53:17 +0000619#endif
David Lawrence Ramsey045883a2004-10-05 20:11:31 +0000620 answer = mallocstrassn(answer, real_dir_from_tilde(answer));
621 load_buffer(answer);
David Lawrence Ramseyd717d4b2004-07-10 16:53:17 +0000622#ifndef NANO_SMALL
David Lawrence Ramsey045883a2004-10-05 20:11:31 +0000623 }
David Lawrence Ramseyd717d4b2004-07-10 16:53:17 +0000624#endif
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000625
Chris Allegretta355fbe52001-07-14 19:32:47 +0000626#ifdef ENABLE_MULTIBUFFER
David Lawrence Ramseyc15802f2004-11-07 18:09:41 +0000627 if (!ISSET(MULTIBUFFER))
628#endif
629 {
630 filestruct *top_save = fileage;
631
David Lawrence Ramsey036a3d42004-11-15 21:49:21 +0000632 /* If we didn't insert into a new buffer, and we were at
633 * the top of the edit window before, set the saved
634 * value of edittop to the new top of the edit window,
635 * and update the current y-coordinate to account for
636 * the number of lines inserted. */
637 if (at_edittop)
638 edittop_save = fileage;
639 current_y += current_y_save;
640
David Lawrence Ramsey56cf0342004-11-15 18:44:30 +0000641 /* If we didn't insert into a new buffer, unpartition
David Lawrence Ramseyc15802f2004-11-07 18:09:41 +0000642 * the filestruct so that it contains all the text
643 * again. Note that we've replaced the non-text
644 * originally in the partition with the text in the
645 * inserted file/executed command output. */
David Lawrence Ramsey74d87072004-11-22 00:16:23 +0000646 unpartition_filestruct(&filepart);
David Lawrence Ramseyc15802f2004-11-07 18:09:41 +0000647
648 /* Renumber starting with the beginning line of the old
649 * partition. */
650 renumber(top_save);
651
652 /* Set edittop back to what it was before. */
653 edittop = edittop_save;
654 }
655
656#ifdef ENABLE_MULTIBUFFER
David Lawrence Ramsey045883a2004-10-05 20:11:31 +0000657 if (ISSET(MULTIBUFFER)) {
658 /* Update the titlebar. */
659 titlebar(NULL);
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000660
David Lawrence Ramsey045883a2004-10-05 20:11:31 +0000661 /* Reinitialize the shortcut list. */
662 shortcut_init(FALSE);
663 } else {
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000664#endif
David Lawrence Ramsey045883a2004-10-05 20:11:31 +0000665 /* Mark the file as modified. */
666 set_modified();
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000667
David Lawrence Ramseyb73c0c02004-11-05 15:25:53 +0000668 /* Restore the old place we want. */
David Lawrence Ramsey31f99362004-11-05 16:01:04 +0000669 placewewant = pww_save;
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000670#ifdef ENABLE_MULTIBUFFER
David Lawrence Ramsey045883a2004-10-05 20:11:31 +0000671 }
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000672#endif
Chris Allegretta4dc03d52002-05-11 03:04:44 +0000673
David Lawrence Ramsey045883a2004-10-05 20:11:31 +0000674 /* Refresh the screen. */
675 edit_refresh();
676
677 break;
678 }
679 } /* while (TRUE) */
Chris Allegretta7662c862003-01-13 01:35:15 +0000680
David Lawrence Ramsey670a56c2004-09-28 16:03:03 +0000681 free(ans);
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000682}
683
David Lawrence Ramsey72e51ab2004-07-02 14:31:03 +0000684void do_insertfile_void(void)
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000685{
Chris Allegretta355fbe52001-07-14 19:32:47 +0000686#ifdef ENABLE_MULTIBUFFER
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000687 if (ISSET(VIEW_MODE) && !ISSET(MULTIBUFFER))
688 statusbar(_("Key illegal in non-multibuffer mode"));
Chris Allegretta32da4562002-01-02 15:12:21 +0000689 else
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000690#endif
David Lawrence Ramseybe908f62004-10-01 18:34:30 +0000691 do_insertfile(
692#ifndef NANO_SMALL
693 FALSE
694#endif
695 );
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000696
697 display_main_list();
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000698}
699
Chris Allegretta355fbe52001-07-14 19:32:47 +0000700#ifdef ENABLE_MULTIBUFFER
Chris Allegretta6df90f52002-07-19 01:08:59 +0000701/* Create a new openfilestruct node. */
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000702openfilestruct *make_new_opennode(void)
Chris Allegretta6df90f52002-07-19 01:08:59 +0000703{
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000704 openfilestruct *newnode =
705 (openfilestruct *)nmalloc(sizeof(openfilestruct));
Chris Allegretta6df90f52002-07-19 01:08:59 +0000706 newnode->filename = NULL;
Chris Allegretta6df90f52002-07-19 01:08:59 +0000707 return newnode;
708}
709
710/* Splice a node into an existing openfilestruct. */
711void splice_opennode(openfilestruct *begin, openfilestruct *newnode,
David Lawrence Ramseyf7080372004-07-08 17:15:10 +0000712 openfilestruct *end)
Chris Allegretta6df90f52002-07-19 01:08:59 +0000713{
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000714 assert(newnode != NULL && begin != NULL);
David Lawrence Ramsey23c44502005-01-27 20:49:07 +0000715
Chris Allegretta6df90f52002-07-19 01:08:59 +0000716 newnode->next = end;
717 newnode->prev = begin;
718 begin->next = newnode;
719 if (end != NULL)
720 end->prev = newnode;
721}
722
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000723/* Unlink a node from the rest of the openfilestruct, and delete it. */
724void unlink_opennode(openfilestruct *fileptr)
Chris Allegretta6df90f52002-07-19 01:08:59 +0000725{
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000726 assert(fileptr != NULL && fileptr->prev != NULL && fileptr->next != NULL && fileptr != fileptr->prev && fileptr != fileptr->next);
David Lawrence Ramsey23c44502005-01-27 20:49:07 +0000727
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000728 fileptr->prev->next = fileptr->next;
729 fileptr->next->prev = fileptr->prev;
730 delete_opennode(fileptr);
Chris Allegretta6df90f52002-07-19 01:08:59 +0000731}
732
733/* Delete a node from the openfilestruct. */
734void delete_opennode(openfilestruct *fileptr)
735{
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000736 assert(fileptr != NULL && fileptr->filename != NULL && fileptr->fileage != NULL);
David Lawrence Ramsey23c44502005-01-27 20:49:07 +0000737
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000738 free(fileptr->filename);
739 free_filestruct(fileptr->fileage);
740 free(fileptr);
Chris Allegretta6df90f52002-07-19 01:08:59 +0000741}
742
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000743#ifdef DEBUG
744/* Deallocate all memory associated with this and later files, including
745 * the lines of text. */
Chris Allegretta6df90f52002-07-19 01:08:59 +0000746void free_openfilestruct(openfilestruct *src)
747{
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000748 assert(src != NULL);
David Lawrence Ramsey23c44502005-01-27 20:49:07 +0000749
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000750 while (src != src->next) {
751 src = src->next;
752 delete_opennode(src->prev);
Chris Allegretta6df90f52002-07-19 01:08:59 +0000753 }
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000754 delete_opennode(src);
Chris Allegretta6df90f52002-07-19 01:08:59 +0000755}
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000756#endif
Chris Allegretta6df90f52002-07-19 01:08:59 +0000757
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000758/* Add/update an entry to the open_files openfilestruct. If update is
David Lawrence Ramsey72e51ab2004-07-02 14:31:03 +0000759 * FALSE, a new entry is created; otherwise, the current entry is
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000760 * updated. */
David Lawrence Ramsey3e189a82004-10-03 19:18:48 +0000761void add_open_file(bool update)
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000762{
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000763 if (open_files == NULL && update)
David Lawrence Ramsey72e51ab2004-07-02 14:31:03 +0000764 return;
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000765
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000766 /* If there are no entries in open_files, make the first one. */
767 if (open_files == NULL) {
768 open_files = make_new_opennode();
769 splice_opennode(open_files, open_files, open_files);
770 /* Otherwise, if we're not updating, make a new entry for
771 * open_files and splice it in after the current entry. */
772 } else if (!update) {
773 splice_opennode(open_files, make_new_opennode(),
774 open_files->next);
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000775 open_files = open_files->next;
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000776 }
777
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000778 /* Save the current filename. */
Chris Allegrettaf2387fb2002-04-10 02:31:20 +0000779 open_files->filename = mallocstrcpy(open_files->filename, filename);
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000780
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +0000781#ifndef NANO_SMALL
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000782 /* Save the current file's stat. */
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +0000783 open_files->originalfilestat = originalfilestat;
784#endif
785
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000786 /* Save the current file buffer. */
787 open_files->fileage = fileage;
788 open_files->filebot = filebot;
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000789
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000790 /* Save the current top of the edit window. */
791 open_files->edittop = edittop;
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000792
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000793 /* Save the current line. */
794 open_files->current = current;
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000795
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000796 /* Save the current cursor position. */
797 open_files->current_x = current_x;
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000798
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000799 /* Save the current place we want. */
800 open_files->placewewant = placewewant;
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000801
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000802 /* Save the current total number of lines. */
803 open_files->totlines = totlines;
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000804
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000805 /* Save the current total size. */
806 open_files->totsize = totsize;
David Lawrence Ramseycb34a672004-02-06 21:20:05 +0000807
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000808 /* Start with no flags saved. */
809 open_files->flags = 0;
810
811 /* Save the current modification status. */
812 if (ISSET(MODIFIED))
813 open_files->flags |= MODIFIED;
814
David Lawrence Ramseya3370c42004-04-05 01:08:14 +0000815#ifndef NANO_SMALL
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000816 /* Save the current marking status and mark, if applicable. */
817 if (ISSET(MARK_ISSET)) {
818 open_files->flags |= MARK_ISSET;
819 open_files->mark_beginbuf = mark_beginbuf;
820 open_files->mark_beginx = mark_beginx;
Chris Allegretta4dc03d52002-05-11 03:04:44 +0000821 }
822
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000823 /* Save the current file format. */
824 open_files->fmt = fmt;
825#endif
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000826
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000827#ifdef DEBUG
Jordi Mallachf9390af2003-08-05 19:31:12 +0000828 fprintf(stderr, "filename is %s\n", open_files->filename);
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000829#endif
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000830}
831
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000832/* Read the current entry in the open_files structure and set up the
David Lawrence Ramsey3ece0b92004-12-01 15:11:27 +0000833 * currently open file buffer using that entry's information. */
David Lawrence Ramsey72e51ab2004-07-02 14:31:03 +0000834void load_open_file(void)
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000835{
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000836 assert(open_files != NULL);
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000837
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000838 /* Restore the current filename. */
Chris Allegrettaf2387fb2002-04-10 02:31:20 +0000839 filename = mallocstrcpy(filename, open_files->filename);
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000840
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +0000841#ifndef NANO_SMALL
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000842 /* Restore the current file's stat. */
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +0000843 originalfilestat = open_files->originalfilestat;
844#endif
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000845
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000846 /* Restore the current file buffer. */
847 fileage = open_files->fileage;
848 filebot = open_files->filebot;
849
850 /* Restore the current top of the edit window. */
851 edittop = open_files->edittop;
852
853 /* Restore the current line. */
854 current = open_files->current;
855
856 /* Restore the current cursor position. */
857 current_x = open_files->current_x;
858
859 /* Restore the current place we want. */
860 placewewant = open_files->placewewant;
861
862 /* Restore the current total number of lines. */
863 totlines = open_files->totlines;
864
865 /* Restore the current total size. */
866 totsize = open_files->totsize;
867
868 /* Restore the current modification status. */
869 if (open_files->flags & MODIFIED)
Chris Allegretta4dc03d52002-05-11 03:04:44 +0000870 SET(MODIFIED);
871 else
872 UNSET(MODIFIED);
873
874#ifndef NANO_SMALL
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000875 /* Restore the current marking status and mark, if applicable. */
876 if (open_files->flags & MARK_ISSET) {
877 mark_beginbuf = open_files->mark_beginbuf;
878 mark_beginx = open_files->mark_beginx;
Chris Allegretta4dc03d52002-05-11 03:04:44 +0000879 SET(MARK_ISSET);
880 } else
881 UNSET(MARK_ISSET);
David Lawrence Ramsey7c1f17a2004-10-03 13:47:26 +0000882
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000883 /* Restore the current file format. */
884 fmt = open_files->fmt;
Chris Allegretta4dc03d52002-05-11 03:04:44 +0000885#endif
Chris Allegrettab3655b42001-10-22 03:15:31 +0000886
Chris Allegretta7662c862003-01-13 01:35:15 +0000887#ifdef ENABLE_COLOR
888 update_color();
889#endif
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000890 edit_refresh();
Chris Allegretta7662c862003-01-13 01:35:15 +0000891
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000892 /* Update the titlebar. */
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000893 clearok(topwin, FALSE);
894 titlebar(NULL);
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000895}
896
David Lawrence Ramsey3ece0b92004-12-01 15:11:27 +0000897/* Open either the next or previous file buffer. */
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000898void open_prevnext_file(bool next)
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000899{
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000900 add_open_file(TRUE);
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000901
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000902 assert(open_files != NULL);
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000903
David Lawrence Ramsey3ece0b92004-12-01 15:11:27 +0000904 /* If only one file buffer is open, indicate it on the statusbar and
905 * get out. */
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000906 if (open_files == open_files->next) {
David Lawrence Ramsey3ece0b92004-12-01 15:11:27 +0000907 statusbar(_("No more open file buffers"));
David Lawrence Ramsey72e51ab2004-07-02 14:31:03 +0000908 return;
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000909 }
910
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000911 /* Switch to the next or previous file, depending on the value of
912 * next. */
913 open_files = next ? open_files->next : open_files->prev;
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000914
915#ifdef DEBUG
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000916 fprintf(stderr, "filename is %s\n", open_files->filename);
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000917#endif
918
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000919 /* Load the file we switched to. */
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000920 load_open_file();
921
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000922 /* And indicate the switch on the statusbar. */
Chris Allegrettaf5de33a2002-02-27 04:14:16 +0000923 statusbar(_("Switched to %s"),
David Lawrence Ramsey3e189a82004-10-03 19:18:48 +0000924 ((open_files->filename[0] == '\0') ? _("New Buffer") :
David Lawrence Ramsey72e51ab2004-07-02 14:31:03 +0000925 open_files->filename));
Chris Allegrettaf5de33a2002-02-27 04:14:16 +0000926
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000927#ifdef DEBUG
928 dump_buffer(current);
929#endif
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000930}
931
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000932/* Open the previous entry in the open_files structure. This function
933 * is used by the shortcut list. */
David Lawrence Ramsey72e51ab2004-07-02 14:31:03 +0000934void open_prevfile_void(void)
Chris Allegrettaa8c22572002-02-15 19:17:02 +0000935{
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000936 open_prevnext_file(FALSE);
Chris Allegrettaa8c22572002-02-15 19:17:02 +0000937}
938
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000939/* Open the next entry in the open_files structure. This function is
940 * used by the shortcut list. */
David Lawrence Ramsey72e51ab2004-07-02 14:31:03 +0000941void open_nextfile_void(void)
Chris Allegrettaa8c22572002-02-15 19:17:02 +0000942{
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000943 open_prevnext_file(TRUE);
Chris Allegrettaa8c22572002-02-15 19:17:02 +0000944}
945
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000946/* Delete an entry from the open_files filestruct. After deletion of an
947 * entry, the next entry is opened. Return TRUE on success or FALSE if
David Lawrence Ramsey3ece0b92004-12-01 15:11:27 +0000948 * there are no more open file buffers. */
David Lawrence Ramsey3e189a82004-10-03 19:18:48 +0000949bool close_open_file(void)
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000950{
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000951 assert(open_files != NULL);
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000952
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000953 /* If only one file is open, get out. */
954 if (open_files == open_files->next)
David Lawrence Ramsey3e189a82004-10-03 19:18:48 +0000955 return FALSE;
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000956
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000957 /* Open the next file. */
958 open_nextfile_void();
Chris Allegretta7162e3d2002-04-06 05:02:14 +0000959
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000960 /* Close the file we had open before. */
961 unlink_opennode(open_files->prev);
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000962
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000963 /* Reinitialize the shortcut list. */
David Lawrence Ramseyebd0d7c2004-07-01 18:59:52 +0000964 shortcut_init(FALSE);
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000965 display_main_list();
David Lawrence Ramsey5b3dd0f2004-11-25 04:39:07 +0000966
David Lawrence Ramsey3e189a82004-10-03 19:18:48 +0000967 return TRUE;
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000968}
David Lawrence Ramsey02517e02004-09-05 21:40:31 +0000969#endif /* ENABLE_MULTIBUFFER */
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000970
David Lawrence Ramseyca018c32004-11-26 20:14:19 +0000971#if !defined(DISABLE_SPELLER) || !defined(DISABLE_OPERATINGDIR)
David Lawrence Ramseyd7ca85c2005-02-09 18:56:21 +0000972/* When passed "[relative path]" or "[relative path][filename]" in
Chris Allegrettae1f14522001-09-19 03:19:43 +0000973 * origpath, return "[full path]" or "[full path][filename]" on success,
David Lawrence Ramseyd7ca85c2005-02-09 18:56:21 +0000974 * or NULL on error. Do this if the file doesn't exist but the relative
975 * path does, since the file could exist in memory but not yet on disk).
976 * Don't do this if the relative path doesn't exist, since we won't be
977 * able to go there. */
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +0000978char *get_full_path(const char *origpath)
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000979{
David Lawrence Ramseyd7ca85c2005-02-09 18:56:21 +0000980 char *d_here, *d_there = NULL;
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000981
David Lawrence Ramseyd7ca85c2005-02-09 18:56:21 +0000982 if (origpath == NULL)
983 return NULL;
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000984
David Lawrence Ramseyd7ca85c2005-02-09 18:56:21 +0000985 /* Get the current directory. */
986#if PATH_MAX != -1
987 d_here = charalloc(PATH_MAX + 1);
988#else
989 d_here = NULL;
990#endif
991 d_here = getcwd(d_here, PATH_MAX + 1);
992#if PATH_MAX != -1
993 align(&d_here);
994#endif
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000995
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +0000996 if (d_here != NULL) {
David Lawrence Ramseyd7ca85c2005-02-09 18:56:21 +0000997 const char *last_slash;
998 char *d_there_file = NULL;
999 bool path_only;
1000 struct stat fileinfo;
Chris Allegretta2d7893d2001-07-11 02:08:33 +00001001
David Lawrence Ramseyd7ca85c2005-02-09 18:56:21 +00001002 /* If the current directory isn't "/", tack a slash onto the end
1003 * of it. */
David Lawrence Ramseyb8c479a2004-07-31 14:10:23 +00001004 if (strcmp(d_here, "/") != 0) {
David Lawrence Ramsey70047ee2003-06-14 20:41:34 +00001005 d_here = charealloc(d_here, strlen(d_here) + 2);
Chris Allegrettace78c1e2001-09-23 01:18:03 +00001006 strcat(d_here, "/");
1007 }
Chris Allegretta2d7893d2001-07-11 02:08:33 +00001008
David Lawrence Ramseyd7ca85c2005-02-09 18:56:21 +00001009 d_there = real_dir_from_tilde(origpath);
Chris Allegrettae1f14522001-09-19 03:19:43 +00001010
David Lawrence Ramseyd7ca85c2005-02-09 18:56:21 +00001011 assert(d_there != NULL);
Chris Allegretta2d7893d2001-07-11 02:08:33 +00001012
David Lawrence Ramseyd7ca85c2005-02-09 18:56:21 +00001013 /* Stat d_there. If stat() fails, assume that d_there refers to
1014 * a new file that hasn't been saved to disk yet. Set path_only
1015 * to TRUE if d_there refers to a directory, and FALSE if
1016 * d_there refers to a file. */
1017 path_only = !stat(d_there, &fileinfo) &&
1018 S_ISDIR(fileinfo.st_mode);
1019
1020 /* If path_only is TRUE, make sure d_there ends in a slash. */
Chris Allegrettae1f14522001-09-19 03:19:43 +00001021 if (path_only) {
David Lawrence Ramseyd7ca85c2005-02-09 18:56:21 +00001022 size_t d_there_len = strlen(d_there);
1023
1024 if (d_there[d_there_len - 1] != '/') {
1025 d_there = charealloc(d_there, d_there_len + 2);
Chris Allegrettae1f14522001-09-19 03:19:43 +00001026 strcat(d_there, "/");
Chris Allegrettae1f14522001-09-19 03:19:43 +00001027 }
1028 }
1029
David Lawrence Ramseyd7ca85c2005-02-09 18:56:21 +00001030 /* Search for the last slash in d_there. */
Chris Allegretta2d7893d2001-07-11 02:08:33 +00001031 last_slash = strrchr(d_there, '/');
1032
David Lawrence Ramseyd7ca85c2005-02-09 18:56:21 +00001033 /* If we didn't find one, then make sure the answer is in the
1034 * format "d_here/d_there". */
1035 if (last_slash == NULL) {
1036 assert(!path_only);
Chris Allegretta2d7893d2001-07-11 02:08:33 +00001037
David Lawrence Ramseyd7ca85c2005-02-09 18:56:21 +00001038 d_there_file = d_there;
1039 d_there = d_here;
1040 } else {
1041 /* If path_only is FALSE, then save the filename portion of
1042 * the answer, everything after the last slash, in
1043 * d_there_file. */
1044 if (!path_only)
1045 d_there_file = mallocstrcpy(NULL, last_slash + 1);
Chris Allegretta2d7893d2001-07-11 02:08:33 +00001046
David Lawrence Ramseyd7ca85c2005-02-09 18:56:21 +00001047 /* And remove the filename portion of the answer from
1048 * d_there. */
1049 null_at(&d_there, last_slash - d_there + 1);
Chris Allegretta2d7893d2001-07-11 02:08:33 +00001050
David Lawrence Ramseyd7ca85c2005-02-09 18:56:21 +00001051 /* Go to the path specified in d_there. */
1052 if (chdir(d_there) == -1) {
Chris Allegretta2d7893d2001-07-11 02:08:33 +00001053 free(d_there);
David Lawrence Ramseyd7ca85c2005-02-09 18:56:21 +00001054 d_there = NULL;
1055 } else {
1056 /* Get the full path and save it in d_there. */
1057 free(d_there);
1058#if PATH_MAX != -1
1059 d_there = charalloc(PATH_MAX + 1);
1060#else
1061 d_there = NULL;
1062#endif
1063 d_there = getcwd(d_there, PATH_MAX + 1);
1064#if PATH_MAX != -1
Chris Allegretta2d7893d2001-07-11 02:08:33 +00001065 align(&d_there);
David Lawrence Ramseyd7ca85c2005-02-09 18:56:21 +00001066#endif
Chris Allegrettace78c1e2001-09-23 01:18:03 +00001067
David Lawrence Ramseyd7ca85c2005-02-09 18:56:21 +00001068 if (d_there == NULL)
1069 /* If we couldn't get the full path, set path_only
1070 * to TRUE so that we clean up correctly, free all
1071 * allocated memory, and return NULL. */
1072 path_only = TRUE;
1073 else if (strcmp(d_there, "/") != 0) {
1074 /* Make sure d_there ends in a slash. */
1075 d_there = charealloc(d_there, strlen(d_there) + 2);
1076 strcat(d_there, "/");
Chris Allegrettae1f14522001-09-19 03:19:43 +00001077 }
David Lawrence Ramseyd7ca85c2005-02-09 18:56:21 +00001078
1079 /* Finally, go back to the path specified in d_here,
1080 * where we were before. */
1081 chdir(d_here);
Chris Allegretta2d7893d2001-07-11 02:08:33 +00001082 }
1083
David Lawrence Ramseyd7ca85c2005-02-09 18:56:21 +00001084 /* Free d_here, since we're done using it. */
1085 free(d_here);
Chris Allegrettae1f14522001-09-19 03:19:43 +00001086 }
Chris Allegretta2d7893d2001-07-11 02:08:33 +00001087
David Lawrence Ramseyd7ca85c2005-02-09 18:56:21 +00001088 /* At this point, if path_only is FALSE and d_there exists,
1089 * d_there contains the path portion of the answer and
1090 * d_there_file contains the filename portion of the answer. If
1091 * this is the case, tack d_there_file onto the end of
1092 * d_there, so that d_there contains the complete answer. */
1093 if (!path_only && d_there) {
1094 d_there = charealloc(d_there, strlen(d_there) +
1095 strlen(d_there_file) + 1);
1096 strcat(d_there, d_there_file);
1097 }
1098
1099 /* Free d_there_file, since we're done using it. */
Chris Allegretta2d7893d2001-07-11 02:08:33 +00001100 free(d_there_file);
Chris Allegretta2d7893d2001-07-11 02:08:33 +00001101 }
1102
David Lawrence Ramseyd7ca85c2005-02-09 18:56:21 +00001103 return d_there;
Chris Allegretta2d7893d2001-07-11 02:08:33 +00001104}
Chris Allegretta48b06702002-02-22 04:30:50 +00001105#endif /* !DISABLE_SPELLER || !DISABLE_OPERATINGDIR */
Chris Allegrettaa8c22572002-02-15 19:17:02 +00001106
1107#ifndef DISABLE_SPELLER
David Lawrence Ramseyd7ca85c2005-02-09 18:56:21 +00001108/* Return the full version of path, as returned by get_full_path(). On
1109 * error, if path doesn't reference a directory, or if the directory
1110 * isn't writable, return NULL. */
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001111char *check_writable_directory(const char *path)
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00001112{
Chris Allegrettaa8c22572002-02-15 19:17:02 +00001113 char *full_path = get_full_path(path);
Chris Allegrettaa8c22572002-02-15 19:17:02 +00001114
David Lawrence Ramseyd7ca85c2005-02-09 18:56:21 +00001115 /* If get_full_path() fails, return NULL. */
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00001116 if (full_path == NULL)
Chris Allegrettaa8c22572002-02-15 19:17:02 +00001117 return NULL;
Chris Allegrettabc72e362002-02-16 20:03:44 +00001118
David Lawrence Ramseyd7ca85c2005-02-09 18:56:21 +00001119 /* If we can't write to path or path isn't a directory, return
1120 * NULL. */
1121 if (access(full_path, W_OK) != 0 ||
1122 full_path[strlen(full_path) - 1] != '/') {
Chris Allegrettabc72e362002-02-16 20:03:44 +00001123 free(full_path);
Chris Allegrettaa8c22572002-02-15 19:17:02 +00001124 return NULL;
Chris Allegrettabc72e362002-02-16 20:03:44 +00001125 }
Chris Allegrettaa8c22572002-02-15 19:17:02 +00001126
1127 /* otherwise, return the full path */
1128 return full_path;
1129}
1130
1131/*
1132 * This function accepts a directory name and filename prefix the same
1133 * way that tempnam() does, determines the location for its temporary
1134 * file the same way that tempnam() does, safely creates the temporary
1135 * file there via mkstemp(), and returns the name of the temporary file
Chris Allegrettabc72e362002-02-16 20:03:44 +00001136 * the same way that tempnam() does. It does not reference the value of
1137 * TMP_MAX because the total number of random filenames that it can
1138 * generate using one prefix is equal to 256**6, which is a sufficiently
1139 * large number to handle most cases. Since the behavior after tempnam()
1140 * generates TMP_MAX random filenames is implementation-defined, my
1141 * implementation is to go on generating random filenames regardless of
1142 * it.
Chris Allegrettaa8c22572002-02-15 19:17:02 +00001143 */
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00001144char *safe_tempnam(const char *dirname, const char *filename_prefix)
1145{
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001146 char *full_tempdir = NULL;
1147 const char *TMPDIR_env;
Chris Allegrettabc72e362002-02-16 20:03:44 +00001148 int filedesc;
Chris Allegrettaa8c22572002-02-15 19:17:02 +00001149
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001150 /* if $TMPDIR is set and non-empty, set tempdir to it, run it through
1151 get_full_path(), and save the result in full_tempdir; otherwise,
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00001152 leave full_tempdir set to NULL */
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001153 TMPDIR_env = getenv("TMPDIR");
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00001154 if (TMPDIR_env != NULL && TMPDIR_env[0] != '\0')
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001155 full_tempdir = check_writable_directory(TMPDIR_env);
Chris Allegrettaa8c22572002-02-15 19:17:02 +00001156
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001157 /* if $TMPDIR is blank or isn't set, or isn't a writable
1158 directory, and dirname isn't NULL, try it; otherwise, leave
1159 full_tempdir set to NULL */
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00001160 if (full_tempdir == NULL && dirname != NULL)
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001161 full_tempdir = check_writable_directory(dirname);
Chris Allegrettaa8c22572002-02-15 19:17:02 +00001162
1163 /* if $TMPDIR is blank or isn't set, or if it isn't a writable
1164 directory, and dirname is NULL, try P_tmpdir instead */
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00001165 if (full_tempdir == NULL)
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001166 full_tempdir = check_writable_directory(P_tmpdir);
Chris Allegrettaa8c22572002-02-15 19:17:02 +00001167
1168 /* if P_tmpdir didn't work, use /tmp instead */
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00001169 if (full_tempdir == NULL) {
Chris Allegrettaa8c22572002-02-15 19:17:02 +00001170 full_tempdir = charalloc(6);
1171 strcpy(full_tempdir, "/tmp/");
1172 }
1173
David Lawrence Ramsey70047ee2003-06-14 20:41:34 +00001174 full_tempdir = charealloc(full_tempdir, strlen(full_tempdir) + 12);
Chris Allegrettaa8c22572002-02-15 19:17:02 +00001175
1176 /* like tempnam(), use only the first 5 characters of the prefix */
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001177 strncat(full_tempdir, filename_prefix, 5);
1178 strcat(full_tempdir, "XXXXXX");
1179 filedesc = mkstemp(full_tempdir);
Chris Allegrettaa8c22572002-02-15 19:17:02 +00001180
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001181 /* if mkstemp succeeded, close the resulting file; afterwards, it'll be
1182 0 bytes long, so delete it; finally, return the filename (all that's
1183 left of it) */
1184 if (filedesc != -1) {
Chris Allegrettaa8c22572002-02-15 19:17:02 +00001185 close(filedesc);
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001186 unlink(full_tempdir);
1187 return full_tempdir;
Chris Allegrettaa8c22572002-02-15 19:17:02 +00001188 }
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001189
1190 free(full_tempdir);
1191 return NULL;
Chris Allegrettaa8c22572002-02-15 19:17:02 +00001192}
1193#endif /* !DISABLE_SPELLER */
Chris Allegrettae1f14522001-09-19 03:19:43 +00001194
1195#ifndef DISABLE_OPERATINGDIR
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001196/* Initialize full_operating_dir based on operating_dir. */
1197void init_operating_dir(void)
1198{
1199 assert(full_operating_dir == NULL);
1200
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00001201 if (operating_dir == NULL)
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001202 return;
David Lawrence Ramsey807681a2004-02-27 20:50:01 +00001203
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001204 full_operating_dir = get_full_path(operating_dir);
1205
1206 /* If get_full_path() failed or the operating directory is
David Lawrence Ramsey95b7bdb2004-02-27 03:21:23 +00001207 * inaccessible, unset operating_dir. */
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00001208 if (full_operating_dir == NULL || chdir(full_operating_dir) == -1) {
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001209 free(full_operating_dir);
1210 full_operating_dir = NULL;
1211 free(operating_dir);
1212 operating_dir = NULL;
1213 }
1214}
1215
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001216/* Check to see if we're inside the operating directory. Return 0 if we
Chris Allegrettae1f14522001-09-19 03:19:43 +00001217 * are, or 1 otherwise. If allow_tabcomp is nonzero, allow incomplete
1218 * names that would be matches for the operating directory, so that tab
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001219 * completion will work. */
David Lawrence Ramsey3e819142004-12-31 04:10:28 +00001220int check_operating_dir(const char *currpath, bool allow_tabcomp)
Chris Allegrettae1f14522001-09-19 03:19:43 +00001221{
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001222 /* The char *full_operating_dir is global for mem cleanup. It
1223 * should have already been initialized by init_operating_dir().
1224 * Also, a relative operating directory path will only be handled
1225 * properly if this is done. */
Chris Allegrettae1f14522001-09-19 03:19:43 +00001226
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001227 char *fullpath;
1228 int retval = 0;
1229 const char *whereami1, *whereami2 = NULL;
Chris Allegrettae1f14522001-09-19 03:19:43 +00001230
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001231 /* If no operating directory is set, don't bother doing anything. */
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00001232 if (operating_dir == NULL)
Chris Allegrettae1f14522001-09-19 03:19:43 +00001233 return 0;
David Lawrence Ramsey6a2877d2004-02-28 02:08:15 +00001234
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001235 assert(full_operating_dir != NULL);
Chris Allegrettae1f14522001-09-19 03:19:43 +00001236
Chris Allegrettae1f14522001-09-19 03:19:43 +00001237 fullpath = get_full_path(currpath);
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001238
1239 /* fullpath == NULL means some directory in the path doesn't exist
1240 * or is unreadable. If allow_tabcomp is zero, then currpath is
1241 * what the user typed somewhere. We don't want to report a
1242 * non-existent directory as being outside the operating directory,
1243 * so we return 0. If allow_tabcomp is nonzero, then currpath
1244 * exists, but is not executable. So we say it isn't in the
1245 * operating directory. */
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00001246 if (fullpath == NULL)
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001247 return allow_tabcomp;
Chris Allegrettae1f14522001-09-19 03:19:43 +00001248
1249 whereami1 = strstr(fullpath, full_operating_dir);
1250 if (allow_tabcomp)
1251 whereami2 = strstr(full_operating_dir, fullpath);
1252
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001253 /* If both searches failed, we're outside the operating directory.
1254 * Otherwise, check the search results; if the full operating
1255 * directory path is not at the beginning of the full current path
1256 * (for normal usage) and vice versa (for tab completion, if we're
1257 * allowing it), we're outside the operating directory. */
Chris Allegrettae1f14522001-09-19 03:19:43 +00001258 if (whereami1 != fullpath && whereami2 != full_operating_dir)
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001259 retval = 1;
1260 free(fullpath);
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001261
1262 /* Otherwise, we're still inside it. */
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001263 return retval;
Chris Allegrettae1f14522001-09-19 03:19:43 +00001264}
Chris Allegretta2d7893d2001-07-11 02:08:33 +00001265#endif
1266
David Lawrence Ramsey04e42a62004-02-28 16:24:31 +00001267#ifndef NANO_SMALL
1268void init_backup_dir(void)
1269{
1270 char *full_backup_dir;
1271
1272 if (backup_dir == NULL)
1273 return;
1274
1275 full_backup_dir = get_full_path(backup_dir);
1276
1277 /* If get_full_path() failed or the backup directory is
1278 * inaccessible, unset backup_dir. */
1279 if (full_backup_dir == NULL ||
1280 full_backup_dir[strlen(full_backup_dir) - 1] != '/') {
1281 free(full_backup_dir);
1282 free(backup_dir);
1283 backup_dir = NULL;
1284 } else {
1285 free(backup_dir);
1286 backup_dir = full_backup_dir;
1287 }
1288}
1289#endif
1290
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001291/* Read from inn, write to out. We assume inn is opened for reading,
1292 * and out for writing. We return 0 on success, -1 on read error, -2 on
1293 * write error. */
1294int copy_file(FILE *inn, FILE *out)
1295{
1296 char buf[BUFSIZ];
1297 size_t charsread;
1298 int retval = 0;
1299
1300 assert(inn != NULL && out != NULL);
1301 do {
1302 charsread = fread(buf, sizeof(char), BUFSIZ, inn);
1303 if (charsread == 0 && ferror(inn)) {
1304 retval = -1;
1305 break;
1306 }
1307 if (fwrite(buf, sizeof(char), charsread, out) < charsread) {
1308 retval = -2;
1309 break;
1310 }
1311 } while (charsread > 0);
1312 if (fclose(inn) == EOF)
1313 retval = -1;
1314 if (fclose(out) == EOF)
1315 retval = -2;
1316 return retval;
1317}
1318
David Lawrence Ramseyc8c69d52004-07-03 03:22:23 +00001319/* Write a file out. If tmp is FALSE, we set the umask to disallow
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001320 * anyone else from accessing the file, we don't set the global variable
1321 * filename to its name, and we don't print out how many lines we wrote
1322 * on the statusbar.
Chris Allegretta7662c862003-01-13 01:35:15 +00001323 *
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001324 * tmp means we are writing a temporary file in a secure fashion. We
1325 * use it when spell checking or dumping the file on an error.
Chris Allegrettacc197ef2001-05-29 04:21:44 +00001326 *
Chris Allegretta0e9b7aa2002-04-16 03:15:47 +00001327 * append == 1 means we are appending instead of overwriting.
1328 * append == 2 means we are prepending instead of overwriting.
Chris Allegrettaecc3d7f2001-06-05 23:24:55 +00001329 *
David Lawrence Ramsey2f0d03b2004-05-28 00:15:28 +00001330 * nonamechange means don't change the current filename. It is ignored
1331 * if tmp is FALSE or if we're appending/prepending.
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001332 *
1333 * Return -1 on error, 1 on success. */
David Lawrence Ramsey8c16bac2004-11-06 15:10:57 +00001334int write_file(const char *name, bool tmp, int append, bool
1335 nonamechange)
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001336{
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001337 int retval = -1;
1338 /* Instead of returning in this function, you should always
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001339 * merely set retval and then goto cleanup_and_exit. */
1340 size_t lineswritten = 0;
1341 const filestruct *fileptr = fileage;
Chris Allegretta0547eb32002-04-22 23:52:34 +00001342 int fd;
David Lawrence Ramsey0af86042004-07-07 23:26:08 +00001343 /* The file descriptor we use. */
David Lawrence Ramsey25529072004-07-06 14:02:44 +00001344 mode_t original_umask = 0;
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001345 /* Our umask, from when nano started. */
David Lawrence Ramsey8c16bac2004-11-06 15:10:57 +00001346 bool realexists;
David Lawrence Ramseyc8c69d52004-07-03 03:22:23 +00001347 /* The result of stat(). TRUE if the file exists, FALSE
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001348 * otherwise. If name is a link that points nowhere, realexists
David Lawrence Ramseyc8c69d52004-07-03 03:22:23 +00001349 * is FALSE. */
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001350 struct stat st;
1351 /* The status fields filled in by stat(). */
David Lawrence Ramsey8c16bac2004-11-06 15:10:57 +00001352 bool anyexists;
David Lawrence Ramsey0af86042004-07-07 23:26:08 +00001353 /* The result of lstat(). Same as realexists unless name is a
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001354 * link. */
1355 struct stat lst;
1356 /* The status fields filled in by lstat(). */
1357 char *realname;
David Lawrence Ramsey0af86042004-07-07 23:26:08 +00001358 /* name after tilde expansion. */
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001359 FILE *f;
1360 /* The actual file, realname, we are writing to. */
1361 char *tempname = NULL;
1362 /* The temp file name we write to on prepend. */
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001363
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001364 assert(name != NULL);
David Lawrence Ramsey951d7142004-10-04 15:23:47 +00001365 if (name[0] == '\0')
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001366 return -1;
Chris Allegrettaa0d89972003-02-03 03:32:08 +00001367 if (!tmp)
1368 titlebar(NULL);
Chris Allegretta0f5dfef2000-11-24 14:02:57 +00001369
Chris Allegrettabe77c612000-11-24 14:00:16 +00001370 realname = real_dir_from_tilde(name);
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001371
Chris Allegrettae1f14522001-09-19 03:19:43 +00001372#ifndef DISABLE_OPERATINGDIR
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001373 /* If we're writing a temporary file, we're probably going outside
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001374 * the operating directory, so skip the operating directory test. */
David Lawrence Ramsey72e51ab2004-07-02 14:31:03 +00001375 if (!tmp && check_operating_dir(realname, FALSE) != 0) {
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001376 statusbar(_("Can't write outside of %s"), operating_dir);
1377 goto cleanup_and_exit;
Chris Allegrettae1f14522001-09-19 03:19:43 +00001378 }
1379#endif
1380
David Lawrence Ramsey8c16bac2004-11-06 15:10:57 +00001381 anyexists = (lstat(realname, &lst) != -1);
David Lawrence Ramseye08765d2004-11-26 20:17:49 +00001382
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001383 /* New case: if the file exists, just give up. */
1384 if (tmp && anyexists)
1385 goto cleanup_and_exit;
David Lawrence Ramseye08765d2004-11-26 20:17:49 +00001386
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001387 /* If NOFOLLOW_SYMLINKS is set, it doesn't make sense to prepend or
1388 * append to a symlink. Here we warn about the contradiction. */
1389 if (ISSET(NOFOLLOW_SYMLINKS) && anyexists && S_ISLNK(lst.st_mode)) {
David Lawrence Ramseyc8c69d52004-07-03 03:22:23 +00001390 statusbar(
1391 _("Cannot prepend or append to a symlink with --nofollow set"));
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001392 goto cleanup_and_exit;
1393 }
1394
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001395 /* Save the state of file at the end of the symlink (if there is
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001396 * one). */
David Lawrence Ramsey8c16bac2004-11-06 15:10:57 +00001397 realexists = (stat(realname, &st) != -1);
Chris Allegrettaf7ee9e62000-12-02 21:13:50 +00001398
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +00001399#ifndef NANO_SMALL
1400 /* We backup only if the backup toggle is set, the file isn't
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001401 * temporary, and the file already exists. Furthermore, if we
1402 * aren't appending, prepending, or writing a selection, we backup
1403 * only if the file has not been modified by someone else since nano
1404 * opened it. */
David Lawrence Ramsey8c16bac2004-11-06 15:10:57 +00001405 if (ISSET(BACKUP_FILE) && !tmp && realexists &&
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001406 (append != 0 || ISSET(MARK_ISSET) ||
1407 originalfilestat.st_mtime == st.st_mtime)) {
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +00001408
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001409 FILE *backup_file;
1410 char *backupname;
1411 struct utimbuf filetime;
1412 int copy_status;
1413
1414 /* Save the original file's access and modification times. */
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +00001415 filetime.actime = originalfilestat.st_atime;
1416 filetime.modtime = originalfilestat.st_mtime;
1417
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001418 /* Open the original file to copy to the backup. */
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +00001419 f = fopen(realname, "rb");
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00001420 if (f == NULL) {
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001421 statusbar(_("Error reading %s: %s"), realname,
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +00001422 strerror(errno));
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001423 goto cleanup_and_exit;
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +00001424 }
1425
David Lawrence Ramsey04e42a62004-02-28 16:24:31 +00001426 /* If backup_dir is set, we set backupname to
1427 * backup_dir/backupname~, where backupnae is the canonicalized
1428 * absolute pathname of realname with every '/' replaced with a
1429 * '!'. This means that /home/foo/file is backed up in
1430 * backup_dir/!home!foo!file~. */
1431 if (backup_dir != NULL) {
1432 char *canon_realname = get_full_path(realname);
1433 size_t i;
1434
1435 if (canon_realname == NULL)
1436 /* If get_full_path() failed, we don't have a
1437 * canonicalized absolute pathname, so just use the
1438 * filename portion of the pathname. We use tail() so
1439 * that e.g. ../backupname will be backed up in
1440 * backupdir/backupname~ instead of
1441 * backupdir/../backupname~. */
1442 canon_realname = mallocstrcpy(NULL, tail(realname));
1443 else {
1444 for (i = 0; canon_realname[i] != '\0'; i++) {
1445 if (canon_realname[i] == '/')
1446 canon_realname[i] = '!';
1447 }
1448 }
1449
1450 backupname = charalloc(strlen(backup_dir) +
1451 strlen(canon_realname) + 2);
1452 sprintf(backupname, "%s%s~", backup_dir, canon_realname);
1453 free(canon_realname);
1454 } else {
1455 backupname = charalloc(strlen(realname) + 2);
1456 sprintf(backupname, "%s~", realname);
1457 }
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +00001458
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001459 /* Open the destination backup file. Before we write to it, we
1460 * set its permissions, so no unauthorized person can read it as
1461 * we write. */
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +00001462 backup_file = fopen(backupname, "wb");
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001463 if (backup_file == NULL ||
1464 chmod(backupname, originalfilestat.st_mode) == -1) {
David Lawrence Ramsey2dbcc802004-11-26 18:10:07 +00001465 statusbar(_("Error writing %s: %s"), backupname,
1466 strerror(errno));
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +00001467 free(backupname);
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001468 if (backup_file != NULL)
1469 fclose(backup_file);
1470 fclose(f);
1471 goto cleanup_and_exit;
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +00001472 }
1473
1474#ifdef DEBUG
Jordi Mallachf9390af2003-08-05 19:31:12 +00001475 fprintf(stderr, "Backing up %s to %s\n", realname, backupname);
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +00001476#endif
1477
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001478 /* Copy the file. */
1479 copy_status = copy_file(f, backup_file);
David Lawrence Ramseye08765d2004-11-26 20:17:49 +00001480
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001481 /* And set metadata. */
David Lawrence Ramsey2dbcc802004-11-26 18:10:07 +00001482 if (copy_status != 0 || chown(backupname,
1483 originalfilestat.st_uid, originalfilestat.st_gid) == -1
1484 || utime(backupname, &filetime) == -1) {
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001485 free(backupname);
1486 if (copy_status == -1)
David Lawrence Ramsey2dbcc802004-11-26 18:10:07 +00001487 statusbar(_("Error reading %s: %s"), realname,
1488 strerror(errno));
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001489 else
1490 statusbar(_("Error writing %s: %s"), backupname,
1491 strerror(errno));
1492 goto cleanup_and_exit;
1493 }
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +00001494 free(backupname);
1495 }
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001496#endif /* !NANO_SMALL */
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +00001497
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001498 /* If NOFOLLOW_SYMLINKS and the file is a link, we aren't doing
1499 * prepend or append. So we delete the link first, and just
1500 * overwrite. */
1501 if (ISSET(NOFOLLOW_SYMLINKS) && anyexists && S_ISLNK(lst.st_mode) &&
1502 unlink(realname) == -1) {
1503 statusbar(_("Error writing %s: %s"), realname, strerror(errno));
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001504 goto cleanup_and_exit;
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001505 }
Chris Allegretta07f9ee02000-12-04 05:15:39 +00001506
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001507 original_umask = umask(0);
1508 umask(original_umask);
David Lawrence Ramseyc8c69d52004-07-03 03:22:23 +00001509
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001510 /* If we create a temp file, we don't let anyone else access it. We
David Lawrence Ramseyc8c69d52004-07-03 03:22:23 +00001511 * create a temp file if tmp is TRUE or if we're prepending. */
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001512 if (tmp || append == 2)
1513 umask(S_IRWXG | S_IRWXO);
1514
David Lawrence Ramseyc8c69d52004-07-03 03:22:23 +00001515 /* If we're prepending, copy the file to a temp file. */
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001516 if (append == 2) {
1517 int fd_source;
1518 FILE *f_source = NULL;
1519
1520 tempname = charalloc(strlen(realname) + 8);
1521 strcpy(tempname, realname);
1522 strcat(tempname, ".XXXXXX");
1523 fd = mkstemp(tempname);
1524 f = NULL;
1525 if (fd != -1) {
1526 f = fdopen(fd, "wb");
1527 if (f == NULL)
1528 close(fd);
1529 }
1530 if (f == NULL) {
David Lawrence Ramsey2dbcc802004-11-26 18:10:07 +00001531 statusbar(_("Error writing %s: %s"), tempname,
1532 strerror(errno));
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001533 unlink(tempname);
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001534 goto cleanup_and_exit;
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001535 }
Chris Allegretta07f9ee02000-12-04 05:15:39 +00001536
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001537 fd_source = open(realname, O_RDONLY | O_CREAT);
1538 if (fd_source != -1) {
1539 f_source = fdopen(fd_source, "rb");
1540 if (f_source == NULL)
1541 close(fd_source);
1542 }
1543 if (f_source == NULL) {
David Lawrence Ramsey2dbcc802004-11-26 18:10:07 +00001544 statusbar(_("Error reading %s: %s"), realname,
1545 strerror(errno));
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001546 fclose(f);
1547 unlink(tempname);
1548 goto cleanup_and_exit;
1549 }
1550
1551 if (copy_file(f_source, f) != 0) {
David Lawrence Ramsey2dbcc802004-11-26 18:10:07 +00001552 statusbar(_("Error writing %s: %s"), tempname,
1553 strerror(errno));
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001554 unlink(tempname);
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001555 goto cleanup_and_exit;
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001556 }
1557 }
1558
David Lawrence Ramseyc8c69d52004-07-03 03:22:23 +00001559 /* Now open the file in place. Use O_EXCL if tmp is TRUE. This is
1560 * now copied from joe, because wiggy says so *shrug*. */
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001561 fd = open(realname, O_WRONLY | O_CREAT |
1562 (append == 1 ? O_APPEND : (tmp ? O_EXCL : O_TRUNC)),
David Lawrence Ramseyc8c69d52004-07-03 03:22:23 +00001563 S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001564
David Lawrence Ramseyc8c69d52004-07-03 03:22:23 +00001565 /* Set the umask back to the user's original value. */
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001566 umask(original_umask);
1567
1568 /* First, just give up if we couldn't even open the file. */
1569 if (fd == -1) {
1570 statusbar(_("Error writing %s: %s"), realname, strerror(errno));
David Lawrence Ramseyca92bec2004-10-22 03:33:54 +00001571 /* tempname has been set only if we're prepending. */
1572 if (tempname != NULL)
1573 unlink(tempname);
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001574 goto cleanup_and_exit;
1575 }
Chris Allegretta0547eb32002-04-22 23:52:34 +00001576
David Lawrence Ramseyf21cd102002-06-13 00:40:19 +00001577 f = fdopen(fd, append == 1 ? "ab" : "wb");
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00001578 if (f == NULL) {
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001579 statusbar(_("Error writing %s: %s"), realname, strerror(errno));
1580 close(fd);
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001581 goto cleanup_and_exit;
Chris Allegretta0547eb32002-04-22 23:52:34 +00001582 }
1583
David Lawrence Ramsey32d19ce2004-05-24 05:05:07 +00001584 /* There might not be a magicline. There won't be when writing out
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001585 * a selection. */
1586 assert(fileage != NULL && filebot != NULL);
1587 while (fileptr != filebot) {
1588 size_t data_len = strlen(fileptr->data);
1589 size_t size;
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001590
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001591 /* Newlines to nulls, just before we write to disk. */
David Lawrence Ramseyf21cd102002-06-13 00:40:19 +00001592 sunder(fileptr->data);
1593
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001594 size = fwrite(fileptr->data, sizeof(char), data_len, f);
David Lawrence Ramseyf21cd102002-06-13 00:40:19 +00001595
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001596 /* Nulls to newlines; data_len is the string's real length. */
David Lawrence Ramseyf21cd102002-06-13 00:40:19 +00001597 unsunder(fileptr->data, data_len);
1598
Chris Allegretta0547eb32002-04-22 23:52:34 +00001599 if (size < data_len) {
David Lawrence Ramsey2dbcc802004-11-26 18:10:07 +00001600 statusbar(_("Error writing %s: %s"), realname,
1601 strerror(errno));
Chris Allegrettab2751752002-04-23 10:22:33 +00001602 fclose(f);
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001603 goto cleanup_and_exit;
Chris Allegrettab2751752002-04-23 10:22:33 +00001604 }
Chris Allegretta91841892001-09-21 02:37:01 +00001605#ifndef NANO_SMALL
David Lawrence Ramsey2dbcc802004-11-26 18:10:07 +00001606 if (fmt == DOS_FILE || fmt == MAC_FILE) {
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001607 if (putc('\r', f) == EOF) {
David Lawrence Ramsey2dbcc802004-11-26 18:10:07 +00001608 statusbar(_("Error writing %s: %s"), realname,
1609 strerror(errno));
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001610 fclose(f);
1611 goto cleanup_and_exit;
1612 }
David Lawrence Ramsey2dbcc802004-11-26 18:10:07 +00001613 }
Chris Allegretta8fa1e282001-09-22 04:20:25 +00001614
David Lawrence Ramsey2dbcc802004-11-26 18:10:07 +00001615 if (fmt != MAC_FILE) {
Chris Allegretta91841892001-09-21 02:37:01 +00001616#endif
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001617 if (putc('\n', f) == EOF) {
David Lawrence Ramsey2dbcc802004-11-26 18:10:07 +00001618 statusbar(_("Error writing %s: %s"), realname,
1619 strerror(errno));
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001620 fclose(f);
1621 goto cleanup_and_exit;
1622 }
David Lawrence Ramsey2dbcc802004-11-26 18:10:07 +00001623#ifndef NANO_SMALL
1624 }
1625#endif
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001626
1627 fileptr = fileptr->next;
1628 lineswritten++;
1629 }
1630
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001631 /* If we're prepending, open the temp file, and append it to f. */
1632 if (append == 2) {
1633 int fd_source;
1634 FILE *f_source = NULL;
David Lawrence Ramseyf21cd102002-06-13 00:40:19 +00001635
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001636 fd_source = open(tempname, O_RDONLY | O_CREAT);
1637 if (fd_source != -1) {
1638 f_source = fdopen(fd_source, "rb");
1639 if (f_source == NULL)
1640 close(fd_source);
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001641 }
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001642 if (f_source == NULL) {
David Lawrence Ramsey2dbcc802004-11-26 18:10:07 +00001643 statusbar(_("Error reading %s: %s"), tempname,
1644 strerror(errno));
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001645 fclose(f);
1646 goto cleanup_and_exit;
1647 }
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001648
David Lawrence Ramsey2c4e34c2004-10-22 03:30:39 +00001649 if (copy_file(f_source, f) == -1 || unlink(tempname) == -1) {
David Lawrence Ramsey2dbcc802004-11-26 18:10:07 +00001650 statusbar(_("Error writing %s: %s"), realname,
1651 strerror(errno));
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001652 goto cleanup_and_exit;
1653 }
1654 } else if (fclose(f) == EOF) {
1655 statusbar(_("Error writing %s: %s"), realname, strerror(errno));
1656 unlink(tempname);
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001657 goto cleanup_and_exit;
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001658 }
1659
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00001660 if (!tmp && append == 0) {
Chris Allegretta7662c862003-01-13 01:35:15 +00001661 if (!nonamechange) {
Chris Allegrettaecc3d7f2001-06-05 23:24:55 +00001662 filename = mallocstrcpy(filename, realname);
Chris Allegretta7662c862003-01-13 01:35:15 +00001663#ifdef ENABLE_COLOR
1664 update_color();
David Lawrence Ramsey760a2dc2004-01-14 06:38:00 +00001665 if (ISSET(COLOR_SYNTAX))
1666 edit_refresh();
Chris Allegretta7662c862003-01-13 01:35:15 +00001667#endif
1668 }
Chris Allegrettaecc3d7f2001-06-05 23:24:55 +00001669
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +00001670#ifndef NANO_SMALL
1671 /* Update originalfilestat to reference the file as it is now. */
1672 stat(filename, &originalfilestat);
1673#endif
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001674 statusbar(P_("Wrote %u line", "Wrote %u lines", lineswritten),
1675 lineswritten);
Chris Allegretta1bd0ce22000-12-06 05:56:08 +00001676 UNSET(MODIFIED);
Chris Allegrettaf4b96012001-01-03 07:11:47 +00001677 titlebar(NULL);
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001678 }
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001679
1680 retval = 1;
1681
1682 cleanup_and_exit:
1683 free(realname);
David Lawrence Ramsey82138502003-12-24 08:03:54 +00001684 free(tempname);
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001685 return retval;
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001686}
1687
David Lawrence Ramsey35961c42004-01-23 19:34:03 +00001688#ifndef NANO_SMALL
1689/* Write a marked selection from a file out. First, set fileage and
1690 * filebot as the top and bottom of the mark, respectively. Then call
David Lawrence Ramsey2f0d03b2004-05-28 00:15:28 +00001691 * write_file() with the values of name, temp, and append, and with
1692 * nonamechange set to TRUE so that we don't change the current
1693 * filename. Finally, set fileage and filebot back to their old values
1694 * and return. */
David Lawrence Ramsey8c16bac2004-11-06 15:10:57 +00001695int write_marked(const char *name, bool tmp, int append)
David Lawrence Ramsey35961c42004-01-23 19:34:03 +00001696{
1697 int retval = -1;
David Lawrence Ramsey7cfea8b2004-10-08 15:35:33 +00001698 bool old_modified = ISSET(MODIFIED);
David Lawrence Ramsey35961c42004-01-23 19:34:03 +00001699 /* write_file() unsets the MODIFIED flag. */
David Lawrence Ramsey40bdb682004-11-03 22:03:41 +00001700 bool added_magicline;
1701 /* Whether we added a magicline after filebot. */
1702 filestruct *top, *bot;
1703 size_t top_x, bot_x;
David Lawrence Ramsey35961c42004-01-23 19:34:03 +00001704
David Lawrence Ramsey40bdb682004-11-03 22:03:41 +00001705 /* Partition the filestruct so that it contains only the marked
1706 * text. */
1707 mark_order((const filestruct **)&top, &top_x,
David Lawrence Ramsey90e59c12004-11-05 23:03:03 +00001708 (const filestruct **)&bot, &bot_x, NULL);
David Lawrence Ramsey40bdb682004-11-03 22:03:41 +00001709 filepart = partition_filestruct(top, top_x, bot, bot_x);
David Lawrence Ramsey35961c42004-01-23 19:34:03 +00001710
1711 /* If the line at filebot is blank, treat it as the magicline and
David Lawrence Ramsey40bdb682004-11-03 22:03:41 +00001712 * hence the end of the file. Otherwise, add a magicline and treat
1713 * it as the end of the file. */
1714 added_magicline = (filebot->data[0] != '\0');
1715 if (added_magicline)
1716 new_magicline();
David Lawrence Ramsey35961c42004-01-23 19:34:03 +00001717
David Lawrence Ramsey2f0d03b2004-05-28 00:15:28 +00001718 retval = write_file(name, tmp, append, TRUE);
David Lawrence Ramsey35961c42004-01-23 19:34:03 +00001719
David Lawrence Ramsey40bdb682004-11-03 22:03:41 +00001720 /* If we added a magicline, remove it now. */
1721 if (added_magicline)
1722 remove_magicline();
1723
1724 /* Unpartition the filestruct so that it contains all the text
1725 * again. */
David Lawrence Ramsey74d87072004-11-22 00:16:23 +00001726 unpartition_filestruct(&filepart);
David Lawrence Ramsey40bdb682004-11-03 22:03:41 +00001727
David Lawrence Ramsey7cfea8b2004-10-08 15:35:33 +00001728 if (old_modified)
David Lawrence Ramsey35961c42004-01-23 19:34:03 +00001729 set_modified();
1730
1731 return retval;
1732}
1733#endif /* !NANO_SMALL */
1734
David Lawrence Ramsey951d7142004-10-04 15:23:47 +00001735int do_writeout(bool exiting)
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001736{
David Lawrence Ramsey6aec4b82004-03-15 20:26:30 +00001737 int i;
David Lawrence Ramsey045883a2004-10-05 20:11:31 +00001738 int retval = 0, append = 0;
1739 char *ans;
1740 /* The last answer the user typed on the statusbar. */
Chris Allegretta8a0de3b2000-11-24 20:45:14 +00001741#ifdef NANO_EXTRA
David Lawrence Ramsey056c5ef2004-10-05 16:14:19 +00001742 static bool did_cred = FALSE;
Chris Allegretta8a0de3b2000-11-24 20:45:14 +00001743#endif
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001744
Chris Allegretta6b58acd2001-04-12 03:01:53 +00001745 currshortcut = writefile_list;
Chris Allegretta6fe61492001-05-21 12:56:25 +00001746
David Lawrence Ramseyedab0cc2004-07-03 03:09:12 +00001747 if (exiting && filename[0] != '\0' && ISSET(TEMP_FILE)) {
David Lawrence Ramsey045883a2004-10-05 20:11:31 +00001748 retval = write_file(filename, FALSE, 0, FALSE);
1749
1750 /* Write succeeded. */
1751 if (retval == 1)
1752 return retval;
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001753 }
1754
Chris Allegretta500b5e32001-06-21 23:58:47 +00001755#ifndef NANO_SMALL
David Lawrence Ramsey6aec4b82004-03-15 20:26:30 +00001756 if (ISSET(MARK_ISSET) && !exiting)
David Lawrence Ramsey045883a2004-10-05 20:11:31 +00001757 ans = mallocstrcpy(NULL, "");
David Lawrence Ramsey6aec4b82004-03-15 20:26:30 +00001758 else
1759#endif
David Lawrence Ramsey045883a2004-10-05 20:11:31 +00001760 ans = mallocstrcpy(NULL, filename);
David Lawrence Ramsey6aec4b82004-03-15 20:26:30 +00001761
1762 while (TRUE) {
1763 const char *msg;
1764#ifndef NANO_SMALL
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001765 const char *formatstr, *backupstr;
1766
David Lawrence Ramsey3e0c8a62004-11-04 04:08:18 +00001767 if (fmt == DOS_FILE)
David Lawrence Ramsey576bf332004-07-12 03:10:30 +00001768 formatstr = N_(" [DOS Format]");
David Lawrence Ramsey3e0c8a62004-11-04 04:08:18 +00001769 else if (fmt == MAC_FILE)
David Lawrence Ramsey360191c2004-10-01 21:37:36 +00001770 formatstr = N_(" [Mac Format]");
Chris Allegrettaa8c22572002-02-15 19:17:02 +00001771 else
1772 formatstr = "";
1773
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +00001774 if (ISSET(BACKUP_FILE))
David Lawrence Ramsey576bf332004-07-12 03:10:30 +00001775 backupstr = N_(" [Backup]");
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +00001776 else
1777 backupstr = "";
1778
David Lawrence Ramsey6aec4b82004-03-15 20:26:30 +00001779 /* Be nice to the translation folks. */
Chris Allegretta9519eb02001-09-27 20:46:25 +00001780 if (ISSET(MARK_ISSET) && !exiting) {
Chris Allegretta0e9b7aa2002-04-16 03:15:47 +00001781 if (append == 2)
David Lawrence Ramsey576bf332004-07-12 03:10:30 +00001782 msg = N_("Prepend Selection to File");
Chris Allegretta7662c862003-01-13 01:35:15 +00001783 else if (append == 1)
David Lawrence Ramsey576bf332004-07-12 03:10:30 +00001784 msg = N_("Append Selection to File");
Chris Allegretta9519eb02001-09-27 20:46:25 +00001785 else
David Lawrence Ramsey576bf332004-07-12 03:10:30 +00001786 msg = N_("Write Selection to File");
David Lawrence Ramsey6aec4b82004-03-15 20:26:30 +00001787 } else
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +00001788#endif /* !NANO_SMALL */
David Lawrence Ramsey6aec4b82004-03-15 20:26:30 +00001789 if (append == 2)
David Lawrence Ramsey576bf332004-07-12 03:10:30 +00001790 msg = N_("File Name to Prepend to");
David Lawrence Ramsey6aec4b82004-03-15 20:26:30 +00001791 else if (append == 1)
David Lawrence Ramsey576bf332004-07-12 03:10:30 +00001792 msg = N_("File Name to Append to");
David Lawrence Ramsey6aec4b82004-03-15 20:26:30 +00001793 else
David Lawrence Ramsey576bf332004-07-12 03:10:30 +00001794 msg = N_("File Name to Write");
David Lawrence Ramsey6aec4b82004-03-15 20:26:30 +00001795
David Lawrence Ramseyf7b5d932004-07-05 14:27:29 +00001796 /* If we're using restricted mode, the filename isn't blank,
1797 * and we're at the "Write File" prompt, disable tab
1798 * completion. */
David Lawrence Ramsey6420d442004-08-11 05:13:08 +00001799 i = statusq(!ISSET(RESTRICTED) || filename[0] == '\0',
David Lawrence Ramsey045883a2004-10-05 20:11:31 +00001800 writefile_list, ans,
David Lawrence Ramsey6aec4b82004-03-15 20:26:30 +00001801#ifndef NANO_SMALL
David Lawrence Ramsey045883a2004-10-05 20:11:31 +00001802 NULL, "%s%s%s", _(msg), formatstr, backupstr
David Lawrence Ramsey6aec4b82004-03-15 20:26:30 +00001803#else
David Lawrence Ramsey045883a2004-10-05 20:11:31 +00001804 "%s", _(msg)
David Lawrence Ramsey6aec4b82004-03-15 20:26:30 +00001805#endif
1806 );
1807
David Lawrence Ramsey951d7142004-10-04 15:23:47 +00001808 if (i < 0) {
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001809 statusbar(_("Cancelled"));
David Lawrence Ramsey045883a2004-10-05 20:11:31 +00001810 retval = -1;
1811 break;
1812 } else {
1813 ans = mallocstrcpy(ans, answer);
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001814
Rocco Corsiaf5c3022001-01-12 07:51:05 +00001815#ifndef DISABLE_BROWSER
David Lawrence Ramsey045883a2004-10-05 20:11:31 +00001816 if (i == NANO_TOFILES_KEY) {
1817 char *tmp = do_browse_from(answer);
Chris Allegretta6fe61492001-05-21 12:56:25 +00001818
David Lawrence Ramsey045883a2004-10-05 20:11:31 +00001819 currshortcut = writefile_list;
1820
1821 if (tmp == NULL)
1822 continue;
1823 free(answer);
1824 answer = tmp;
1825
1826 /* We have a file now. Get out of the statusbar prompt
1827 * cleanly. */
1828 statusq_abort();
1829 } else
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001830#endif /* !DISABLE_BROWSER */
Chris Allegrettacf287c82002-07-20 13:57:41 +00001831#ifndef NANO_SMALL
David Lawrence Ramsey045883a2004-10-05 20:11:31 +00001832 if (i == TOGGLE_DOS_KEY) {
David Lawrence Ramsey3e0c8a62004-11-04 04:08:18 +00001833 fmt = (fmt == DOS_FILE) ? NIX_FILE : DOS_FILE;
David Lawrence Ramsey045883a2004-10-05 20:11:31 +00001834 continue;
1835 } else if (i == TOGGLE_MAC_KEY) {
David Lawrence Ramsey3e0c8a62004-11-04 04:08:18 +00001836 fmt = (fmt == MAC_FILE) ? NIX_FILE : MAC_FILE;
David Lawrence Ramsey045883a2004-10-05 20:11:31 +00001837 continue;
1838 } else if (i == TOGGLE_BACKUP_KEY) {
1839 TOGGLE(BACKUP_FILE);
1840 continue;
1841 } else
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00001842#endif /* !NANO_SMALL */
David Lawrence Ramsey045883a2004-10-05 20:11:31 +00001843 if (i == NANO_PREPEND_KEY) {
1844 append = (append == 2) ? 0 : 2;
1845 continue;
1846 } else if (i == NANO_APPEND_KEY) {
1847 append = (append == 1) ? 0 : 1;
1848 continue;
1849 }
Chris Allegrettaf4b96012001-01-03 07:11:47 +00001850
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001851#ifdef DEBUG
David Lawrence Ramsey045883a2004-10-05 20:11:31 +00001852 fprintf(stderr, "filename is %s\n", answer);
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001853#endif
Chris Allegretta8a0de3b2000-11-24 20:45:14 +00001854
1855#ifdef NANO_EXTRA
David Lawrence Ramsey045883a2004-10-05 20:11:31 +00001856 if (exiting && !ISSET(TEMP_FILE) &&
1857 strcasecmp(answer, "zzy") == 0 && !did_cred) {
1858 do_credits();
1859 did_cred = TRUE;
1860 retval = -1;
1861 break;
David Lawrence Ramseybc503c82003-11-19 23:59:14 +00001862 }
David Lawrence Ramsey045883a2004-10-05 20:11:31 +00001863#endif
1864 if (append == 0 && strcmp(answer, filename) != 0) {
1865 struct stat st;
1866
1867 if (!stat(answer, &st)) {
1868 i = do_yesno(FALSE, _("File exists, OVERWRITE ? "));
1869 if (i == 0 || i == -1)
1870 continue;
1871 /* If we're using restricted mode, we aren't allowed to
1872 * change the name of a file once it has one because
1873 * that would allow reading from or writing to files not
1874 * specified on the command line. In this case, don't
1875 * bother showing the "Different Name" prompt. */
1876 } else if (!ISSET(RESTRICTED) && filename[0] != '\0'
1877#ifndef NANO_SMALL
1878 && (exiting || !ISSET(MARK_ISSET))
1879#endif
1880 ) {
David Lawrence Ramsey2dbcc802004-11-26 18:10:07 +00001881 i = do_yesno(FALSE,
1882 _("Save file under DIFFERENT NAME ? "));
David Lawrence Ramsey045883a2004-10-05 20:11:31 +00001883 if (i == 0 || i == -1)
1884 continue;
1885 }
1886 }
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001887
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +00001888#ifndef NANO_SMALL
David Lawrence Ramsey045883a2004-10-05 20:11:31 +00001889 /* Here's where we allow the selected text to be written to
1890 * a separate file. If we're using restricted mode, this is
1891 * disabled since it allows reading from or writing to files
1892 * not specified on the command line. */
1893 if (!ISSET(RESTRICTED) && !exiting && ISSET(MARK_ISSET))
1894 retval = write_marked(answer, FALSE, append);
1895 else
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +00001896#endif /* !NANO_SMALL */
David Lawrence Ramsey045883a2004-10-05 20:11:31 +00001897 retval = write_file(answer, FALSE, append, FALSE);
Chris Allegretta2d7893d2001-07-11 02:08:33 +00001898
Chris Allegretta355fbe52001-07-14 19:32:47 +00001899#ifdef ENABLE_MULTIBUFFER
David Lawrence Ramsey045883a2004-10-05 20:11:31 +00001900 /* If we're not about to exit, update the current entry in
1901 * the open_files structure. */
1902 if (!exiting)
1903 add_open_file(TRUE);
Chris Allegretta2d7893d2001-07-11 02:08:33 +00001904#endif
David Lawrence Ramsey045883a2004-10-05 20:11:31 +00001905
1906 break;
1907 }
David Lawrence Ramsey6aec4b82004-03-15 20:26:30 +00001908 } /* while (TRUE) */
David Lawrence Ramsey045883a2004-10-05 20:11:31 +00001909
1910 free(ans);
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00001911
David Lawrence Ramsey045883a2004-10-05 20:11:31 +00001912 return retval;
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001913}
1914
David Lawrence Ramsey72e51ab2004-07-02 14:31:03 +00001915void do_writeout_void(void)
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001916{
David Lawrence Ramsey72e51ab2004-07-02 14:31:03 +00001917 do_writeout(FALSE);
David Lawrence Ramsey045883a2004-10-05 20:11:31 +00001918 display_main_list();
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001919}
Chris Allegretta04d848e2000-11-05 17:54:41 +00001920
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00001921/* Return a malloc()ed string containing the actual directory, used to
1922 * convert ~user/ and ~/ notation. */
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001923char *real_dir_from_tilde(const char *buf)
Chris Allegrettabe77c612000-11-24 14:00:16 +00001924{
Chris Allegrettad865da12002-07-29 23:46:38 +00001925 char *dirtmp = NULL;
Chris Allegrettae5e4d492001-01-23 03:09:15 +00001926
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00001927 if (buf == NULL)
1928 return NULL;
1929
Chris Allegrettabe77c612000-11-24 14:00:16 +00001930 if (buf[0] == '~') {
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001931 size_t i;
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00001932 const char *tilde_dir;
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001933
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00001934 /* Figure out how much of the str we need to compare. */
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001935 for (i = 1; buf[i] != '/' && buf[i] != '\0'; i++)
1936 ;
1937
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00001938 /* Get the home directory. */
1939 if (i == 1) {
1940 get_homedir();
1941 tilde_dir = homedir;
1942 } else {
1943 const struct passwd *userdata;
1944
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001945 do {
1946 userdata = getpwent();
1947 } while (userdata != NULL &&
David Lawrence Ramsey2dbcc802004-11-26 18:10:07 +00001948 strncmp(userdata->pw_name, buf + 1, i - 1) != 0);
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00001949 endpwent();
1950 tilde_dir = userdata->pw_dir;
Chris Allegrettad865da12002-07-29 23:46:38 +00001951 }
Chris Allegretta04fec912000-11-25 04:43:43 +00001952
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00001953 if (tilde_dir != NULL) {
1954 dirtmp = charalloc(strlen(tilde_dir) + strlen(buf + i) + 1);
1955 sprintf(dirtmp, "%s%s", tilde_dir, buf + i);
Chris Allegrettabe77c612000-11-24 14:00:16 +00001956 }
Chris Allegrettae5e4d492001-01-23 03:09:15 +00001957 }
Chris Allegrettabe77c612000-11-24 14:00:16 +00001958
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00001959 /* Set a default value for dirtmp, in case the user's home directory
1960 * isn't found. */
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001961 if (dirtmp == NULL)
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00001962 dirtmp = mallocstrcpy(NULL, buf);
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001963
Chris Allegretta1bd0ce22000-12-06 05:56:08 +00001964 return dirtmp;
Chris Allegrettabe77c612000-11-24 14:00:16 +00001965}
1966
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00001967#if !defined(DISABLE_TABCOMP) || !defined(DISABLE_BROWSER)
1968/* Our sort routine for file listings. Sort directories before
1969 * filenames, alphabetically and ignoring case differences. Sort
1970 * filenames the same way, except for ignoring an initial dot. */
1971int diralphasort(const void *va, const void *vb)
1972{
1973 struct stat fileinfo;
1974 const char *a = *(const char *const *)va;
1975 const char *b = *(const char *const *)vb;
1976 bool aisdir = stat(a, &fileinfo) != -1 && S_ISDIR(fileinfo.st_mode);
1977 bool bisdir = stat(b, &fileinfo) != -1 && S_ISDIR(fileinfo.st_mode);
1978
1979 if (aisdir && !bisdir)
1980 return -1;
1981 if (!aisdir && bisdir)
1982 return 1;
1983
1984 if (*a == '.')
1985 a++;
1986 if (*b == '.')
1987 b++;
1988
1989 return strcasecmp(a, b);
1990}
1991#endif
1992
Chris Allegretta7662c862003-01-13 01:35:15 +00001993#ifndef DISABLE_TABCOMP
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00001994/* Is the given file a directory? */
1995int is_dir(const char *buf)
Chris Allegrettabe77c612000-11-24 14:00:16 +00001996{
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00001997 char *dirptr = real_dir_from_tilde(buf);
Chris Allegrettabe77c612000-11-24 14:00:16 +00001998 struct stat fileinfo;
1999
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00002000 int ret = (stat(dirptr, &fileinfo) != -1 &&
2001 S_ISDIR(fileinfo.st_mode));
Chris Allegrettabe77c612000-11-24 14:00:16 +00002002
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00002003 assert(buf != NULL && dirptr != buf);
Chris Allegrettabe77c612000-11-24 14:00:16 +00002004
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00002005 free(dirptr);
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00002006
Chris Allegretta04fec912000-11-25 04:43:43 +00002007 return ret;
Chris Allegrettabe77c612000-11-24 14:00:16 +00002008}
Chris Allegretta04d848e2000-11-05 17:54:41 +00002009
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00002010/* These functions (username_tab_completion(), cwd_tab_completion(), and
Chris Allegretta7662c862003-01-13 01:35:15 +00002011 * input_tab()) were taken from busybox 0.46 (cmdedit.c). Here is the
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00002012 * notice from that file:
Chris Allegretta04d848e2000-11-05 17:54:41 +00002013 *
2014 * Termios command line History and Editting, originally
2015 * intended for NetBSD sh (ash)
2016 * Copyright (c) 1999
2017 * Main code: Adam Rogoyski <rogoyski@cs.utexas.edu>
2018 * Etc: Dave Cinege <dcinege@psychosis.com>
2019 * Majorly adjusted/re-written for busybox:
2020 * Erik Andersen <andersee@debian.org>
2021 *
2022 * You may use this code as you wish, so long as the original author(s)
2023 * are attributed in any redistributions of the source code.
2024 * This code is 'as is' with no warranty.
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00002025 * This code may safely be consumed by a BSD or GPL license. */
Chris Allegretta04d848e2000-11-05 17:54:41 +00002026
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00002027/* We consider the first buflen characters of buf for ~username tab
2028 * completion. */
2029char **username_tab_completion(const char *buf, size_t *num_matches,
2030 size_t buflen)
Chris Allegretta04d848e2000-11-05 17:54:41 +00002031{
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00002032 char **matches = NULL;
2033 const struct passwd *userdata;
2034
2035 assert(buf != NULL && num_matches != NULL && buflen > 0);
Chris Allegrettabe77c612000-11-24 14:00:16 +00002036
Chris Allegretta8d9b11a2001-01-19 04:17:24 +00002037 *num_matches = 0;
Chris Allegretta8d9b11a2001-01-19 04:17:24 +00002038
Chris Allegretta2c2c5f22001-01-23 03:27:31 +00002039 while ((userdata = getpwent()) != NULL) {
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00002040 if (strncmp(userdata->pw_name, buf + 1, buflen - 1) == 0) {
2041 /* Cool, found a match. Add it to the list. This makes a
2042 * lot more sense to me (Chris) this way... */
Chris Allegretta8d9b11a2001-01-19 04:17:24 +00002043
Chris Allegrettae1f14522001-09-19 03:19:43 +00002044#ifndef DISABLE_OPERATINGDIR
2045 /* ...unless the match exists outside the operating
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00002046 * directory, in which case just go to the next match. */
2047 if (check_operating_dir(userdata->pw_dir, TRUE))
2048 continue;
Chris Allegrettae1f14522001-09-19 03:19:43 +00002049#endif
2050
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00002051 matches = (char **)nrealloc(matches, (*num_matches + 1) *
2052 sizeof(char *));
2053 matches[*num_matches] =
2054 charalloc(strlen(userdata->pw_name) + 2);
2055 sprintf(matches[*num_matches], "~%s", userdata->pw_name);
David Lawrence Ramseyfd3039a2004-07-17 19:49:12 +00002056 ++(*num_matches);
Chris Allegrettabe77c612000-11-24 14:00:16 +00002057 }
Chris Allegretta2c2c5f22001-01-23 03:27:31 +00002058 }
2059 endpwent();
Chris Allegretta8d9b11a2001-01-19 04:17:24 +00002060
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00002061 return matches;
Chris Allegretta04d848e2000-11-05 17:54:41 +00002062}
2063
2064/* This was originally called exe_n_cwd_tab_completion, but we're not
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00002065 * worried about executables, only filenames :> */
2066char **cwd_tab_completion(const char *buf, size_t *num_matches, size_t
2067 buflen)
Chris Allegretta04d848e2000-11-05 17:54:41 +00002068{
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00002069 char *dirname = mallocstrcpy(NULL, buf);
2070 char *filename;
2071#ifndef DISABLE_OPERATINGDIR
2072 size_t dirnamelen;
2073#endif
2074 size_t filenamelen;
2075 char **matches = NULL;
Chris Allegretta04d848e2000-11-05 17:54:41 +00002076 DIR *dir;
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00002077 const struct dirent *next;
Chris Allegretta04d848e2000-11-05 17:54:41 +00002078
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00002079 assert(dirname != NULL && num_matches != NULL && buflen >= 0);
Chris Allegretta04d848e2000-11-05 17:54:41 +00002080
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00002081 *num_matches = 0;
2082 null_at(&dirname, buflen);
Chris Allegretta04d848e2000-11-05 17:54:41 +00002083
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00002084 /* Okie, if there's a / in the buffer, strip out the directory
2085 * part. */
2086 filename = strrchr(dirname, '/');
2087 if (filename != NULL) {
2088 char *tmpdirname = filename + 1;
Chris Allegretta442f2c52000-11-14 17:46:06 +00002089
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00002090 filename = mallocstrcpy(NULL, tmpdirname);
2091 *tmpdirname = '\0';
2092 tmpdirname = dirname;
2093 dirname = real_dir_from_tilde(dirname);
2094 free(tmpdirname);
Chris Allegretta04d848e2000-11-05 17:54:41 +00002095 } else {
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00002096 filename = dirname;
2097 dirname = mallocstrcpy(NULL, "./");
Chris Allegretta04d848e2000-11-05 17:54:41 +00002098 }
2099
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00002100 assert(dirname[strlen(dirname) - 1] == '/');
Chris Allegrettabe77c612000-11-24 14:00:16 +00002101
Chris Allegrettacf287c82002-07-20 13:57:41 +00002102 dir = opendir(dirname);
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00002103
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00002104 if (dir == NULL) {
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00002105 /* Don't print an error, just shut up and return. */
Chris Allegretta04d848e2000-11-05 17:54:41 +00002106 beep();
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00002107 free(filename);
2108 free(dirname);
2109 return NULL;
Chris Allegretta04d848e2000-11-05 17:54:41 +00002110 }
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00002111
2112#ifndef DISABLE_OPERATINGDIR
2113 dirnamelen = strlen(dirname);
2114#endif
2115 filenamelen = strlen(filename);
2116
Chris Allegretta04d848e2000-11-05 17:54:41 +00002117 while ((next = readdir(dir)) != NULL) {
2118
Chris Allegretta04d848e2000-11-05 17:54:41 +00002119#ifdef DEBUG
Chris Allegretta2c975222000-11-15 01:25:42 +00002120 fprintf(stderr, "Comparing \'%s\'\n", next->d_name);
Chris Allegretta04d848e2000-11-05 17:54:41 +00002121#endif
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00002122 /* See if this matches. */
2123 if (strncmp(next->d_name, filename, filenamelen) == 0 &&
2124 (*filename == '.' || (strcmp(next->d_name, ".") != 0 &&
2125 strcmp(next->d_name, "..") != 0))) {
2126 /* Cool, found a match. Add it to the list. This makes a
2127 * lot more sense to me (Chris) this way... */
Chris Allegrettae1f14522001-09-19 03:19:43 +00002128
2129#ifndef DISABLE_OPERATINGDIR
2130 /* ...unless the match exists outside the operating
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00002131 * directory, in which case just go to the next match. To
2132 * properly do operating directory checking, we have to add
2133 * the directory name to the beginning of the proposed match
2134 * before we check it. */
2135 char *tmp2 = charalloc(strlen(dirname) +
2136 strlen(next->d_name) + 1);
Chris Allegrettae1f14522001-09-19 03:19:43 +00002137
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00002138 sprintf(tmp2, "%s%s", dirname, next->d_name);
2139 if (check_operating_dir(tmp2, TRUE)) {
2140 free(tmp2);
2141 continue;
Chris Allegrettae1f14522001-09-19 03:19:43 +00002142 }
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00002143 free(tmp2);
Chris Allegrettae1f14522001-09-19 03:19:43 +00002144#endif
2145
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00002146 matches = (char **)nrealloc(matches, (*num_matches + 1) *
2147 sizeof(char *));
2148 matches[*num_matches] = mallocstrcpy(NULL, next->d_name);
2149 ++(*num_matches);
Chris Allegretta04d848e2000-11-05 17:54:41 +00002150 }
2151 }
Chris Allegretta3cdf6ff2003-02-08 02:02:02 +00002152 closedir(dir);
2153 free(dirname);
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00002154 free(filename);
Chris Allegretta04d848e2000-11-05 17:54:41 +00002155
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00002156 return matches;
Chris Allegretta04d848e2000-11-05 17:54:41 +00002157}
2158
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00002159/* Do tab completion. This function now has an arg which refers to how
2160 * much the statusbar cursor position (place) should be advanced. */
2161char *input_tab(char *buf, size_t *place, bool *lastwastab, bool *list)
Chris Allegretta04d848e2000-11-05 17:54:41 +00002162{
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00002163 size_t num_matches = 0;
2164 char **matches = NULL;
Chris Allegretta04d848e2000-11-05 17:54:41 +00002165
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00002166 assert(buf != NULL && place != NULL && *place <= strlen(buf) && lastwastab != NULL && list != NULL);
Chris Allegretta2084acc2001-11-29 03:43:08 +00002167
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00002168 *list = 0;
Chris Allegretta04d848e2000-11-05 17:54:41 +00002169
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00002170 /* If the word starts with `~' and there is no slash in the word,
2171 * then try completing this word as a username. */
2172 if (*place > 0 && *buf == '~') {
2173 const char *bob = strchr(buf, '/');
Chris Allegrettab5b89ae2000-11-14 18:25:26 +00002174
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00002175 if (bob == NULL || bob >= buf + *place)
2176 matches = username_tab_completion(buf, &num_matches,
2177 *place);
2178 }
Chris Allegrettae118acc2000-11-06 05:40:03 +00002179
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00002180 /* Match against files relative to the current working directory. */
2181 if (matches == NULL)
2182 matches = cwd_tab_completion(buf, &num_matches, *place);
Chris Allegretta04d848e2000-11-05 17:54:41 +00002183
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00002184 if (num_matches <= 0)
2185 beep();
2186 else {
2187 size_t match, common_len = 0;
2188 size_t lastslash = strrchrn(buf, '/', *place);
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00002189 char *mzero;
Chris Allegretta04d848e2000-11-05 17:54:41 +00002190
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00002191 while (TRUE) {
2192 for (match = 1; match < num_matches; match++) {
2193 if (matches[0][common_len] !=
2194 matches[match][common_len])
2195 break;
2196 }
Chris Allegretta04d848e2000-11-05 17:54:41 +00002197
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00002198 if (match < num_matches || matches[0][common_len] == '\0')
Chris Allegretta04fec912000-11-25 04:43:43 +00002199 break;
Chris Allegretta442f2c52000-11-14 17:46:06 +00002200
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00002201 common_len++;
2202 }
Chris Allegretta442f2c52000-11-14 17:46:06 +00002203
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00002204 mzero = charalloc(lastslash + common_len + 1);
2205 sprintf(mzero, "%.*s%.*s", lastslash, buf, common_len,
2206 matches[0]);
Chris Allegretta442f2c52000-11-14 17:46:06 +00002207
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00002208 common_len += lastslash;
Chris Allegrettae1f14522001-09-19 03:19:43 +00002209
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00002210 assert(common_len >= *place);
Chris Allegrettabe77c612000-11-24 14:00:16 +00002211
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00002212 if (num_matches == 1 && is_dir(mzero)) {
2213 mzero[common_len] = '/';
2214 common_len++;
2215 assert(common_len > *place);
2216 }
Chris Allegrettaec58a992000-11-05 21:54:23 +00002217
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00002218 if (num_matches > 1 && (common_len != *place ||
2219 *lastwastab == FALSE))
2220 beep();
Chris Allegretta442f2c52000-11-14 17:46:06 +00002221
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00002222 /* If there is more match to display on the statusbar, show it.
2223 * We reset lastwastab to FALSE: it requires hitting Tab twice
2224 * in succession with no statusbar changes to see a match
2225 * list. */
2226 if (common_len != *place) {
2227 size_t buflen = strlen(buf);
Chris Allegretta442f2c52000-11-14 17:46:06 +00002228
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00002229 *lastwastab = FALSE;
2230 buf = charealloc(buf, common_len + buflen - *place + 1);
2231 charmove(buf + common_len, buf + *place, buflen - *place + 1);
2232 strncpy(buf, mzero, common_len);
2233 *place = common_len;
2234 } else if (*lastwastab == FALSE || num_matches < 2)
2235 *lastwastab = TRUE;
2236 else {
2237 int longest_name = 0, editline = 0;
2238 size_t columns;
Chris Allegrettaec58a992000-11-05 21:54:23 +00002239
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00002240 /* Now we show a list of the available choices. */
2241 assert(num_matches > 1);
2242
2243 /* Sort the list. */
2244 qsort(matches, num_matches, sizeof(char *), diralphasort);
2245
2246 for (match = 0; match < num_matches; match++) {
2247 common_len = strnlenpt(matches[match], COLS - 1);
2248 if (common_len > COLS - 1) {
2249 longest_name = COLS - 1;
Chris Allegrettaec58a992000-11-05 21:54:23 +00002250 break;
2251 }
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00002252 if (common_len > longest_name)
2253 longest_name = common_len;
Chris Allegrettaec58a992000-11-05 21:54:23 +00002254 }
Chris Allegretta04d848e2000-11-05 17:54:41 +00002255
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00002256 assert(longest_name <= COLS - 1);
2257
2258 /* Each column will be longest_name + 2 characters wide,
2259 * i.e, two spaces between columns, except that there will
2260 * be only one space after the last column. */
2261 columns = (COLS + 1) / (longest_name + 2);
2262
2263 /* Blank the edit window, and print the matches out
2264 * there. */
Chris Allegretta04d848e2000-11-05 17:54:41 +00002265 blank_edit();
2266 wmove(edit, 0, 0);
2267
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00002268 /* Disable el cursor. */
2269 curs_set(0);
Chris Allegretta2c975222000-11-15 01:25:42 +00002270
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00002271 for (match = 0; match < num_matches; match++) {
2272 char *disp;
Chris Allegrettaec58a992000-11-05 21:54:23 +00002273
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00002274 wmove(edit, editline, (longest_name + 2) *
2275 (match % columns));
Chris Allegrettaec58a992000-11-05 21:54:23 +00002276
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00002277 if (match % columns == 0 && editline == editwinrows - 1
2278 && num_matches - match > columns) {
2279 waddstr(edit, _("(more)"));
2280 break;
Chris Allegretta04d848e2000-11-05 17:54:41 +00002281 }
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00002282
2283 disp = display_string(matches[match], 0, longest_name,
2284 FALSE);
2285 waddstr(edit, disp);
2286 free(disp);
2287
2288 if ((match + 1) % columns == 0)
2289 editline++;
Chris Allegretta04d848e2000-11-05 17:54:41 +00002290 }
2291 wrefresh(edit);
David Lawrence Ramsey4d44d2d2004-08-01 22:35:31 +00002292 *list = TRUE;
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00002293 }
2294
2295 free(mzero);
Chris Allegretta04d848e2000-11-05 17:54:41 +00002296 }
2297
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00002298 free_charptrarray(matches, num_matches);
2299
Chris Allegretta2084acc2001-11-29 03:43:08 +00002300 /* Only refresh the edit window if we don't have a list of filename
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00002301 * matches on it. */
David Lawrence Ramsey4d44d2d2004-08-01 22:35:31 +00002302 if (*list == FALSE)
Chris Allegretta2084acc2001-11-29 03:43:08 +00002303 edit_refresh();
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00002304
2305 /* Enable el cursor. */
Chris Allegretta442f2c52000-11-14 17:46:06 +00002306 curs_set(1);
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00002307
Chris Allegretta442f2c52000-11-14 17:46:06 +00002308 return buf;
Chris Allegretta04d848e2000-11-05 17:54:41 +00002309}
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00002310#endif /* !DISABLE_TABCOMP */
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002311
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00002312/* Only print the last part of a path. Isn't there a shell command for
2313 * this? */
David Lawrence Ramsey04e42a62004-02-28 16:24:31 +00002314const char *tail(const char *foo)
2315{
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00002316 const char *tmp = strrchr(foo, '/');
David Lawrence Ramsey04e42a62004-02-28 16:24:31 +00002317
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00002318 if (tmp == NULL)
2319 tmp = foo;
2320 else if (*tmp == '/')
David Lawrence Ramsey04e42a62004-02-28 16:24:31 +00002321 tmp++;
2322
2323 return tmp;
2324}
David Lawrence Ramsey04e42a62004-02-28 16:24:31 +00002325
Rocco Corsiaf5c3022001-01-12 07:51:05 +00002326#ifndef DISABLE_BROWSER
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00002327/* Free our malloc()ed memory. */
David Lawrence Ramseyd7fd2002004-05-18 01:20:36 +00002328void free_charptrarray(char **array, size_t len)
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002329{
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00002330 for (; len > 0; len--)
2331 free(array[len - 1]);
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002332 free(array);
2333}
2334
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00002335/* Strip one dir from the end of a string. */
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002336void striponedir(char *foo)
2337{
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00002338 char *tmp;
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002339
Chris Allegrettae9b5c6f2003-02-12 23:49:56 +00002340 assert(foo != NULL);
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002341
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00002342 tmp = strrchr(foo, '/');
2343 if (tmp != NULL)
2344 *tmp = '\0';
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002345}
2346
Chris Allegretta858d9d92003-01-30 00:53:32 +00002347int readable_dir(const char *path)
2348{
2349 DIR *dir = opendir(path);
2350
2351 /* If dir is NULL, don't do closedir(), since that changes errno. */
2352 if (dir != NULL)
2353 closedir(dir);
2354 return dir != NULL;
2355}
2356
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00002357/* Initialize the browser code, including the list of files in *path */
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00002358char **browser_init(const char *path, int *longest, int *numents)
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00002359{
2360 DIR *dir;
2361 struct dirent *next;
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00002362 char **filelist;
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00002363 int i = 0;
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00002364 size_t path_len;
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00002365
2366 dir = opendir(path);
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00002367 if (dir == NULL)
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00002368 return NULL;
2369
2370 *numents = 0;
2371 while ((next = readdir(dir)) != NULL) {
David Lawrence Ramseyb8c479a2004-07-31 14:10:23 +00002372 if (strcmp(next->d_name, ".") == 0)
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00002373 continue;
2374 (*numents)++;
2375 if (strlen(next->d_name) > *longest)
2376 *longest = strlen(next->d_name);
2377 }
2378 rewinddir(dir);
2379 *longest += 10;
2380
David Lawrence Ramsey70047ee2003-06-14 20:41:34 +00002381 filelist = (char **)nmalloc(*numents * sizeof (char *));
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00002382
David Lawrence Ramseyb8c479a2004-07-31 14:10:23 +00002383 if (strcmp(path, "/") == 0)
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00002384 path = "";
2385 path_len = strlen(path);
2386
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00002387 while ((next = readdir(dir)) != NULL) {
David Lawrence Ramseyb8c479a2004-07-31 14:10:23 +00002388 if (strcmp(next->d_name, ".") == 0)
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00002389 continue;
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00002390
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00002391 filelist[i] = charalloc(strlen(next->d_name) + path_len + 2);
2392 sprintf(filelist[i], "%s/%s", path, next->d_name);
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00002393 i++;
2394 }
Chris Allegrettae9b5c6f2003-02-12 23:49:56 +00002395 closedir(dir);
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00002396
2397 if (*longest > COLS - 1)
2398 *longest = COLS - 1;
2399
2400 return filelist;
2401}
2402
Chris Allegretta0e9d3c62001-01-04 04:59:17 +00002403/* Our browser function. inpath is the path to start browsing from */
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00002404char *do_browser(const char *inpath)
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002405{
2406 struct stat st;
2407 char *foo, *retval = NULL;
2408 static char *path = NULL;
David Lawrence Ramseya0b5ba22004-08-25 15:39:10 +00002409 int numents = 0, i = 0, j = 0, longest = 0, abort = 0, col = 0;
2410 int selected = 0, editline = 0, width = 0, filecols = 0, lineno = 0;
2411 int kbinput = ERR;
David Lawrence Ramseyeb16f432004-09-27 01:04:50 +00002412 bool meta_key, func_key;
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00002413 char **filelist = (char **)NULL;
David Lawrence Ramseyf5b256b2003-10-03 20:26:25 +00002414#ifndef DISABLE_MOUSE
Chris Allegretta051fc6e2001-05-05 23:17:36 +00002415 MEVENT mevent;
2416#endif
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002417
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00002418 assert(inpath != NULL);
2419
Chris Allegretta0e9d3c62001-01-04 04:59:17 +00002420 /* If path isn't the same as inpath, we are being passed a new
2421 dir as an arg. We free it here so it will be copied from
2422 inpath below */
David Lawrence Ramseyb8c479a2004-07-31 14:10:23 +00002423 if (path != NULL && strcmp(path, inpath) != 0) {
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002424 free(path);
2425 path = NULL;
2426 }
2427
Chris Allegretta0e9d3c62001-01-04 04:59:17 +00002428 /* if path doesn't exist, make it so */
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002429 if (path == NULL)
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00002430 path = mallocstrcpy(NULL, inpath);
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002431
2432 filelist = browser_init(path, &longest, &numents);
Chris Allegretta88b09152001-05-17 11:35:43 +00002433 foo = charalloc(longest + 8);
Chris Allegretta0e9d3c62001-01-04 04:59:17 +00002434
David Lawrence Ramsey65e6ecb2005-02-08 20:37:53 +00002435 /* Sort the list. */
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002436 qsort(filelist, numents, sizeof(char *), diralphasort);
2437
2438 titlebar(path);
Chris Allegrettaa8c22572002-02-15 19:17:02 +00002439 bottombars(browser_list);
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002440 curs_set(0);
2441 wmove(edit, 0, 0);
2442 i = 0;
2443 width = 0;
2444 filecols = 0;
Chris Allegretta0e9d3c62001-01-04 04:59:17 +00002445
2446 /* Loop invariant: Microsoft sucks. */
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002447 do {
Chris Allegrettaf80a59c2003-01-30 00:57:33 +00002448 char *new_path;
2449 /* Used by the Go To Directory prompt. */
Rocco Corsi12f294c2001-04-14 06:50:24 +00002450
David Lawrence Ramseye5d8f322004-09-30 22:07:21 +00002451 check_statusblank();
Rocco Corsi12f294c2001-04-14 06:50:24 +00002452
Chris Allegrettab10283c2001-05-07 12:08:37 +00002453 currshortcut = browser_list;
Chris Allegretta6fe61492001-05-21 12:56:25 +00002454
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002455 editline = 0;
2456 col = 0;
Chris Allegrettab0f52822001-01-04 05:20:23 +00002457
Chris Allegretta88520c92001-05-05 17:45:54 +00002458 /* Compute line number we're on now, so we don't divide by zero later */
Chris Allegrettadffa2072002-07-24 01:02:26 +00002459 lineno = selected;
2460 if (width != 0)
2461 lineno /= width;
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002462
2463 switch (kbinput) {
Chris Allegretta6b58acd2001-04-12 03:01:53 +00002464
David Lawrence Ramseyf5b256b2003-10-03 20:26:25 +00002465#ifndef DISABLE_MOUSE
Chris Allegrettadffa2072002-07-24 01:02:26 +00002466 case KEY_MOUSE:
Chris Allegretta051fc6e2001-05-05 23:17:36 +00002467 if (getmouse(&mevent) == ERR)
Chris Allegrettadffa2072002-07-24 01:02:26 +00002468 return retval;
Chris Allegretta051fc6e2001-05-05 23:17:36 +00002469
2470 /* If they clicked in the edit window, they probably clicked
2471 on a file */
Chris Allegrettadffa2072002-07-24 01:02:26 +00002472 if (wenclose(edit, mevent.y, mevent.x)) {
Chris Allegretta051fc6e2001-05-05 23:17:36 +00002473 int selectedbackup = selected;
2474
2475 mevent.y -= 2;
2476
Chris Allegrettadffa2072002-07-24 01:02:26 +00002477 /* Longest is the width of each column. There are two
2478 * spaces between each column. */
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00002479 selected = (lineno / editwinrows) * editwinrows * width
Chris Allegrettadffa2072002-07-24 01:02:26 +00002480 + mevent.y * width + mevent.x / (longest + 2);
2481
2482 /* If they clicked beyond the end of a row, select the
2483 * end of that row. */
2484 if (mevent.x > width * (longest + 2))
2485 selected--;
Chris Allegretta051fc6e2001-05-05 23:17:36 +00002486
2487 /* If we're off the screen, reset to the last item.
2488 If we clicked where we did last time, select this name! */
Chris Allegrettaf3fde7c2001-05-06 03:02:21 +00002489 if (selected > numents - 1)
Chris Allegretta051fc6e2001-05-05 23:17:36 +00002490 selected = numents - 1;
Chris Allegrettadffa2072002-07-24 01:02:26 +00002491 else if (selectedbackup == selected)
David Lawrence Ramsey63e73cb2004-11-28 04:52:57 +00002492 /* Put back the 'select' key */
David Lawrence Ramsey74835712004-12-04 17:41:52 +00002493 unget_kbinput('s', FALSE, FALSE);
David Lawrence Ramseyc59979f2004-10-23 02:47:39 +00002494 } else {
2495 /* Must be clicking a shortcut */
David Lawrence Ramseya0b5ba22004-08-25 15:39:10 +00002496 int mouse_x, mouse_y;
2497 get_mouseinput(&mouse_x, &mouse_y, TRUE);
2498 }
Chris Allegretta051fc6e2001-05-05 23:17:36 +00002499
Chris Allegretta6b58acd2001-04-12 03:01:53 +00002500 break;
2501#endif
David Lawrence Ramseyc2c5a512004-01-23 19:26:17 +00002502 case NANO_PREVLINE_KEY:
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002503 if (selected - width >= 0)
2504 selected -= width;
2505 break;
Chris Allegrettaa2c02e92001-07-02 01:31:44 +00002506 case NANO_BACK_KEY:
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002507 if (selected > 0)
2508 selected--;
2509 break;
David Lawrence Ramseyc2c5a512004-01-23 19:26:17 +00002510 case NANO_NEXTLINE_KEY:
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002511 if (selected + width <= numents - 1)
2512 selected += width;
2513 break;
Chris Allegrettaa2c02e92001-07-02 01:31:44 +00002514 case NANO_FORWARD_KEY:
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002515 if (selected < numents - 1)
2516 selected++;
2517 break;
2518 case NANO_PREVPAGE_KEY:
Chris Allegretta6e827412001-01-04 04:41:08 +00002519 case NANO_PREVPAGE_FKEY:
David Lawrence Ramseyf4276942003-12-24 03:33:09 +00002520 case '-': /* Pico compatibility */
Chris Allegrettadffa2072002-07-24 01:02:26 +00002521 if (selected >= (editwinrows + lineno % editwinrows) * width)
David Lawrence Ramseyc3724882004-05-27 18:39:16 +00002522 selected -= (editwinrows + lineno % editwinrows) * width;
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002523 else
2524 selected = 0;
2525 break;
2526 case NANO_NEXTPAGE_KEY:
Chris Allegretta6e827412001-01-04 04:41:08 +00002527 case NANO_NEXTPAGE_FKEY:
David Lawrence Ramseyf4276942003-12-24 03:33:09 +00002528 case ' ': /* Pico compatibility */
Chris Allegrettadffa2072002-07-24 01:02:26 +00002529 selected += (editwinrows - lineno % editwinrows) * width;
2530 if (selected >= numents)
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002531 selected = numents - 1;
2532 break;
Chris Allegrettab3655b42001-10-22 03:15:31 +00002533 case NANO_HELP_KEY:
2534 case NANO_HELP_FKEY:
David Lawrence Ramseyf4276942003-12-24 03:33:09 +00002535 case '?': /* Pico compatibility */
David Lawrence Ramsey00d77982004-08-07 21:27:37 +00002536#ifndef DISABLE_HELP
David Lawrence Ramseye7638ea2004-06-01 19:49:38 +00002537 do_help();
David Lawrence Ramseyae064bf2004-06-01 20:38:00 +00002538 curs_set(0);
David Lawrence Ramsey00d77982004-08-07 21:27:37 +00002539#else
2540 nano_disabled_msg();
2541#endif
David Lawrence Ramseye7638ea2004-06-01 19:49:38 +00002542 break;
Chris Allegrettaa2c02e92001-07-02 01:31:44 +00002543 case NANO_ENTER_KEY:
David Lawrence Ramseyf4276942003-12-24 03:33:09 +00002544 case 'S': /* Pico compatibility */
2545 case 's':
Chris Allegretta0e9d3c62001-01-04 04:59:17 +00002546 /* You can't cd up from / */
David Lawrence Ramseyb8c479a2004-07-31 14:10:23 +00002547 if (strcmp(filelist[selected], "/..") == 0 &&
2548 strcmp(path, "/") == 0) {
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002549 statusbar(_("Can't move up a directory"));
Chris Allegretta0eab2362003-02-03 03:39:05 +00002550 beep();
Rocco Corsi12f294c2001-04-14 06:50:24 +00002551 break;
2552 }
2553
Chris Allegrettae1f14522001-09-19 03:19:43 +00002554#ifndef DISABLE_OPERATINGDIR
David Lawrence Ramseyf4276942003-12-24 03:33:09 +00002555 /* Note: the selected file can be outside the operating
2556 * directory if it is .. or if it is a symlink to
2557 * directory outside the operating directory. */
David Lawrence Ramsey72e51ab2004-07-02 14:31:03 +00002558 if (check_operating_dir(filelist[selected], FALSE) != 0) {
David Lawrence Ramsey36dd87b2004-07-18 17:43:43 +00002559 statusbar(_("Can't go outside of %s in restricted mode"),
2560 operating_dir);
Chris Allegretta0eab2362003-02-03 03:39:05 +00002561 beep();
2562 break;
Chris Allegrettae1f14522001-09-19 03:19:43 +00002563 }
2564#endif
2565
Chris Allegretta0eab2362003-02-03 03:39:05 +00002566 if (stat(filelist[selected], &st) == -1) {
David Lawrence Ramsey36dd87b2004-07-18 17:43:43 +00002567 statusbar(_("Can't open \"%s\": %s"), filelist[selected],
2568 strerror(errno));
Chris Allegretta0eab2362003-02-03 03:39:05 +00002569 beep();
2570 break;
Chris Allegrettaf5de33a2002-02-27 04:14:16 +00002571 }
2572
Chris Allegretta0eab2362003-02-03 03:39:05 +00002573 if (!S_ISDIR(st.st_mode)) {
2574 retval = mallocstrcpy(retval, filelist[selected]);
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002575 abort = 1;
Chris Allegretta0eab2362003-02-03 03:39:05 +00002576 break;
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002577 }
Chris Allegretta0eab2362003-02-03 03:39:05 +00002578
2579 new_path = mallocstrcpy(NULL, filelist[selected]);
2580
2581 if (strcmp("..", tail(new_path)) == 0) {
2582 /* They want to go up a level, so strip off .. and the
2583 current dir */
2584 striponedir(new_path);
2585 /* SPK for '.' path, get the current path via getcwd */
2586 if (strcmp(new_path, ".") == 0) {
2587 free(new_path);
2588 new_path = getcwd(NULL, PATH_MAX + 1);
2589 }
2590 striponedir(new_path);
2591 }
2592
2593 if (!readable_dir(new_path)) {
2594 /* We can't open this dir for some reason. Complain */
David Lawrence Ramsey36dd87b2004-07-18 17:43:43 +00002595 statusbar(_("Can't open \"%s\": %s"), new_path,
2596 strerror(errno));
Chris Allegretta0eab2362003-02-03 03:39:05 +00002597 free(new_path);
2598 break;
2599 }
2600
2601 free_charptrarray(filelist, numents);
2602 free(foo);
2603 free(path);
2604 path = new_path;
2605 return do_browser(path);
2606
David Lawrence Ramsey6420d442004-08-11 05:13:08 +00002607 /* Go to a specific directory */
David Lawrence Ramseye5d8f322004-09-30 22:07:21 +00002608 case NANO_GOTOLINE_KEY:
2609 case NANO_GOTOLINE_FKEY:
David Lawrence Ramseyf4276942003-12-24 03:33:09 +00002610 case 'G': /* Pico compatibility */
2611 case 'g':
Rocco Corsi12f294c2001-04-14 06:50:24 +00002612 curs_set(1);
David Lawrence Ramsey6aec4b82004-03-15 20:26:30 +00002613 j = statusq(FALSE, gotodir_list, "",
Chris Allegretta7662c862003-01-13 01:35:15 +00002614#ifndef NANO_SMALL
David Lawrence Ramsey6aec4b82004-03-15 20:26:30 +00002615 NULL,
Chris Allegretta7662c862003-01-13 01:35:15 +00002616#endif
David Lawrence Ramsey6420d442004-08-11 05:13:08 +00002617 _("Go To Directory"));
Chris Allegrettaa8c22572002-02-15 19:17:02 +00002618 bottombars(browser_list);
Rocco Corsi12f294c2001-04-14 06:50:24 +00002619 curs_set(0);
2620
2621 if (j < 0) {
David Lawrence Ramsey6420d442004-08-11 05:13:08 +00002622 statusbar(_("Cancelled"));
Rocco Corsi12f294c2001-04-14 06:50:24 +00002623 break;
2624 }
2625
Chris Allegrettaf80a59c2003-01-30 00:57:33 +00002626 new_path = real_dir_from_tilde(answer);
Rocco Corsi12f294c2001-04-14 06:50:24 +00002627
Chris Allegrettaf80a59c2003-01-30 00:57:33 +00002628 if (new_path[0] != '/') {
2629 new_path = charealloc(new_path, strlen(path) + strlen(answer) + 2);
2630 sprintf(new_path, "%s/%s", path, answer);
Rocco Corsi12f294c2001-04-14 06:50:24 +00002631 }
2632
Chris Allegrettaf80a59c2003-01-30 00:57:33 +00002633#ifndef DISABLE_OPERATINGDIR
David Lawrence Ramsey72e51ab2004-07-02 14:31:03 +00002634 if (check_operating_dir(new_path, FALSE) != 0) {
Chris Allegrettaf80a59c2003-01-30 00:57:33 +00002635 statusbar(_("Can't go outside of %s in restricted mode"), operating_dir);
2636 free(new_path);
2637 break;
2638 }
2639#endif
2640
2641 if (!readable_dir(new_path)) {
Rocco Corsi12f294c2001-04-14 06:50:24 +00002642 /* We can't open this dir for some reason. Complain */
2643 statusbar(_("Can't open \"%s\": %s"), answer, strerror(errno));
Chris Allegrettaf80a59c2003-01-30 00:57:33 +00002644 free(new_path);
Rocco Corsi12f294c2001-04-14 06:50:24 +00002645 break;
Chris Allegrettaf80a59c2003-01-30 00:57:33 +00002646 }
Rocco Corsi12f294c2001-04-14 06:50:24 +00002647
2648 /* Start over again with the new path value */
Chris Allegrettafdcb9e92003-02-12 02:52:04 +00002649 free_charptrarray(filelist, numents);
2650 free(foo);
Chris Allegrettaf80a59c2003-01-30 00:57:33 +00002651 free(path);
2652 path = new_path;
Rocco Corsi12f294c2001-04-14 06:50:24 +00002653 return do_browser(path);
2654
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002655 /* Stuff we want to abort the browser */
Chris Allegrettaa2c02e92001-07-02 01:31:44 +00002656 case NANO_CANCEL_KEY:
David Lawrence Ramsey4d7c2602003-08-17 02:48:43 +00002657 case NANO_EXIT_KEY:
Chris Allegretta6e827412001-01-04 04:41:08 +00002658 case NANO_EXIT_FKEY:
David Lawrence Ramseyf4276942003-12-24 03:33:09 +00002659 case 'E': /* Pico compatibility */
2660 case 'e':
Chris Allegrettadffa2072002-07-24 01:02:26 +00002661 abort = 1;
2662 break;
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002663 }
2664 if (abort)
2665 break;
2666
Rocco Corsi12f294c2001-04-14 06:50:24 +00002667 blank_edit();
2668
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00002669 if (width != 0)
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002670 i = width * editwinrows * ((selected / width) / editwinrows);
2671 else
2672 i = 0;
2673
2674 wmove(edit, 0, 0);
2675 for (j = i; j < numents && editline <= editwinrows - 1; j++) {
2676 filecols++;
2677
2678 strncpy(foo, tail(filelist[j]), strlen(tail(filelist[j])) + 1);
2679 while (strlen(foo) < longest)
2680 strcat(foo, " ");
2681 col += strlen(foo);
2682
2683 /* Put file info in the string also */
Chris Allegretta88520c92001-05-05 17:45:54 +00002684 /* We use lstat here to detect links; then, if we find a
2685 symlink, we examine it via stat() to see if it is a
Chris Allegretta0876dc92001-03-28 13:04:08 +00002686 directory or just a file symlink */
2687 lstat(filelist[j], &st);
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002688 if (S_ISDIR(st.st_mode))
2689 strcpy(foo + longest - 5, "(dir)");
2690 else {
Chris Allegretta0876dc92001-03-28 13:04:08 +00002691 if (S_ISLNK(st.st_mode)) {
2692 /* Aha! It's a symlink! Now, is it a dir? If so,
2693 mark it as such */
David Lawrence Ramsey3af54d32004-02-25 04:43:27 +00002694 stat(filelist[j], &st);
Chris Allegretta0876dc92001-03-28 13:04:08 +00002695 if (S_ISDIR(st.st_mode))
2696 strcpy(foo + longest - 5, "(dir)");
2697 else
2698 strcpy(foo + longest - 2, "--");
Chris Allegretta90d30742001-04-03 01:02:38 +00002699 } else if (st.st_size < (1 << 10)) /* less than 1 K */
2700 sprintf(foo + longest - 7, "%4d B",
Chris Allegrettad4615622001-05-23 21:54:47 +00002701 (int) st.st_size);
Chris Allegretta90d30742001-04-03 01:02:38 +00002702 else if (st.st_size >= (1 << 30)) /* at least 1 gig */
2703 sprintf(foo + longest - 7, "%4d GB",
2704 (int) st.st_size >> 30);
2705 else if (st.st_size >= (1 << 20)) /* at least 1 meg */
2706 sprintf(foo + longest - 7, "%4d MB",
2707 (int) st.st_size >> 20);
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00002708 else /* It's more than 1 k and less than a meg */
Chris Allegretta90d30742001-04-03 01:02:38 +00002709 sprintf(foo + longest - 7, "%4d KB",
2710 (int) st.st_size >> 10);
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002711 }
2712
David Lawrence Ramsey0084eaa2002-11-04 16:05:42 +00002713 /* Highlight the currently selected file/dir */
Chris Allegrettadffa2072002-07-24 01:02:26 +00002714 if (j == selected)
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002715 wattron(edit, A_REVERSE);
Chris Allegrettadffa2072002-07-24 01:02:26 +00002716 waddstr(edit, foo);
2717 if (j == selected)
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002718 wattroff(edit, A_REVERSE);
2719
Chris Allegretta0e9d3c62001-01-04 04:59:17 +00002720 /* And add some space between the cols */
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002721 waddstr(edit, " ");
2722 col += 2;
2723
2724 /* And if the next entry isn't going to fit on the
2725 line, move to the next one */
Chris Allegrettadffa2072002-07-24 01:02:26 +00002726 if (col > COLS - longest) {
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002727 editline++;
2728 wmove(edit, editline, 0);
2729 col = 0;
2730 if (width == 0)
2731 width = filecols;
2732 }
2733 }
David Lawrence Ramseya0b5ba22004-08-25 15:39:10 +00002734 wrefresh(edit);
David Lawrence Ramseyeb16f432004-09-27 01:04:50 +00002735 } while ((kbinput = get_kbinput(edit, &meta_key, &func_key)) !=
2736 NANO_EXIT_KEY && kbinput != NANO_EXIT_FKEY);
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002737 curs_set(1);
2738 blank_edit();
Chris Allegretta4dc03d52002-05-11 03:04:44 +00002739 titlebar(NULL);
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002740 edit_refresh();
2741
Chris Allegretta0e9d3c62001-01-04 04:59:17 +00002742 /* cleanup */
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002743 free_charptrarray(filelist, numents);
2744 free(foo);
2745 return retval;
2746}
Chris Allegretta150469a2001-01-05 21:13:14 +00002747
Chris Allegretta88520c92001-05-05 17:45:54 +00002748/* Browser front end, checks to see if inpath has a dir in it and, if so,
Chris Allegretta150469a2001-01-05 21:13:14 +00002749 starts do_browser from there, else from the current dir */
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00002750char *do_browse_from(const char *inpath)
Chris Allegretta150469a2001-01-05 21:13:14 +00002751{
2752 struct stat st;
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00002753 char *bob;
Chris Allegretta858d9d92003-01-30 00:53:32 +00002754 /* The result of do_browser; the selected file name. */
2755 char *path;
2756 /* inpath, tilde expanded. */
Chris Allegretta90d30742001-04-03 01:02:38 +00002757
Chris Allegretta858d9d92003-01-30 00:53:32 +00002758 assert(inpath != NULL);
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00002759
Chris Allegretta858d9d92003-01-30 00:53:32 +00002760 path = real_dir_from_tilde(inpath);
2761
2762 /*
2763 * Perhaps path is a directory. If so, we will pass that to
2764 * do_browser. Otherwise, perhaps path is a directory / a file. So
2765 * we try stripping off the last path element. If it still isn't a
2766 * directory, just use the current directory. */
2767
2768 if (stat(path, &st) == -1 || !S_ISDIR(st.st_mode)) {
2769 striponedir(path);
Chris Allegrettae9b5c6f2003-02-12 23:49:56 +00002770 if (stat(path, &st) == -1 || !S_ISDIR(st.st_mode)) {
2771 free(path);
Chris Allegretta858d9d92003-01-30 00:53:32 +00002772 path = getcwd(NULL, PATH_MAX + 1);
Chris Allegrettae9b5c6f2003-02-12 23:49:56 +00002773 }
Chris Allegretta90d30742001-04-03 01:02:38 +00002774 }
Chris Allegretta150469a2001-01-05 21:13:14 +00002775
Chris Allegretta858d9d92003-01-30 00:53:32 +00002776#ifndef DISABLE_OPERATINGDIR
2777 /* If the resulting path isn't in the operating directory, use that. */
David Lawrence Ramsey72e51ab2004-07-02 14:31:03 +00002778 if (check_operating_dir(path, FALSE) != 0)
Chris Allegretta858d9d92003-01-30 00:53:32 +00002779 path = mallocstrcpy(path, operating_dir);
2780#endif
Chris Allegretta150469a2001-01-05 21:13:14 +00002781
Chris Allegretta858d9d92003-01-30 00:53:32 +00002782 if (!readable_dir(path)) {
2783 beep();
2784 bob = NULL;
2785 } else
2786 bob = do_browser(path);
2787 free(path);
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00002788 return bob;
Chris Allegretta150469a2001-01-05 21:13:14 +00002789}
Chris Allegrettacf287c82002-07-20 13:57:41 +00002790#endif /* !DISABLE_BROWSER */
Chris Allegrettaf3de8b52003-01-16 23:44:46 +00002791
David Lawrence Ramsey6420d442004-08-11 05:13:08 +00002792#if !defined(NANO_SMALL) && defined(ENABLE_NANORC)
David Lawrence Ramseya27bd652004-08-17 05:23:38 +00002793/* Return $HOME/.nano_history, or NULL if we can't find the homedir.
2794 * The string is dynamically allocated, and should be freed. */
2795char *histfilename(void)
2796{
2797 char *nanohist = NULL;
2798
2799 if (homedir != NULL) {
2800 size_t homelen = strlen(homedir);
2801
2802 nanohist = charalloc(homelen + 15);
2803 strcpy(nanohist, homedir);
2804 strcpy(nanohist + homelen, "/.nano_history");
2805 }
2806 return nanohist;
2807}
2808
Chris Allegrettaf3de8b52003-01-16 23:44:46 +00002809void load_history(void)
2810{
David Lawrence Ramseya27bd652004-08-17 05:23:38 +00002811 char *nanohist = histfilename();
Chris Allegrettaf3de8b52003-01-16 23:44:46 +00002812
David Lawrence Ramsey6420d442004-08-11 05:13:08 +00002813 /* assume do_rcfile() has reported missing home dir */
David Lawrence Ramseya27bd652004-08-17 05:23:38 +00002814 if (nanohist != NULL) {
2815 FILE *hist = fopen(nanohist, "r");
Chris Allegrettaf3de8b52003-01-16 23:44:46 +00002816
David Lawrence Ramsey417b03a2003-09-06 21:44:37 +00002817 if (hist == NULL) {
David Lawrence Ramsey6420d442004-08-11 05:13:08 +00002818 if (errno != ENOENT) {
Chris Allegrettad8451932003-03-11 03:50:40 +00002819 /* Don't save history when we quit. */
2820 UNSET(HISTORYLOG);
David Lawrence Ramsey6420d442004-08-11 05:13:08 +00002821 rcfile_error(N_("Error reading %s: %s"), nanohist, strerror(errno));
David Lawrence Ramsey7dfec432004-08-12 15:20:14 +00002822 fprintf(stderr, _("\nPress Return to continue starting nano\n"));
2823 while (getchar() != '\n')
2824 ;
Chris Allegrettad8451932003-03-11 03:50:40 +00002825 }
Chris Allegrettaf3de8b52003-01-16 23:44:46 +00002826 } else {
David Lawrence Ramseya27bd652004-08-17 05:23:38 +00002827 historyheadtype *history = &search_history;
2828 char *line = NULL;
2829 size_t buflen = 0;
2830 ssize_t read;
2831
2832 while ((read = getline(&line, &buflen, hist)) >= 0) {
2833 if (read > 0 && line[read - 1] == '\n') {
2834 read--;
2835 line[read] = '\0';
2836 }
2837 if (read > 0) {
2838 unsunder(line, read);
2839 update_history(history, line);
2840 } else
Chris Allegrettaf3de8b52003-01-16 23:44:46 +00002841 history = &replace_history;
2842 }
2843 fclose(hist);
David Lawrence Ramseya27bd652004-08-17 05:23:38 +00002844 free(line);
Chris Allegrettaf3de8b52003-01-16 23:44:46 +00002845 UNSET(HISTORY_CHANGED);
2846 }
David Lawrence Ramseya27bd652004-08-17 05:23:38 +00002847 free(nanohist);
Chris Allegrettaf3de8b52003-01-16 23:44:46 +00002848 }
2849}
2850
David Lawrence Ramseya27bd652004-08-17 05:23:38 +00002851bool writehist(FILE *hist, historyheadtype *histhead)
2852{
2853 historytype *h;
2854
2855 /* write oldest first */
2856 for (h = histhead->tail; h->prev != NULL; h = h->prev) {
2857 size_t len = strlen(h->data);
2858
2859 sunder(h->data);
2860 if (fwrite(h->data, sizeof(char), len, hist) < len ||
2861 putc('\n', hist) == EOF)
2862 return FALSE;
2863 }
2864 return TRUE;
2865}
2866
Chris Allegrettaf3de8b52003-01-16 23:44:46 +00002867/* save histories to ~/.nano_history */
2868void save_history(void)
2869{
David Lawrence Ramseya27bd652004-08-17 05:23:38 +00002870 char *nanohist;
Chris Allegrettaf3de8b52003-01-16 23:44:46 +00002871
2872 /* don't save unchanged or empty histories */
David Lawrence Ramsey576bf332004-07-12 03:10:30 +00002873 if ((search_history.count == 0 && replace_history.count == 0) ||
David Lawrence Ramsey6420d442004-08-11 05:13:08 +00002874 !ISSET(HISTORY_CHANGED) || ISSET(VIEW_MODE))
Chris Allegrettaf3de8b52003-01-16 23:44:46 +00002875 return;
2876
David Lawrence Ramseya27bd652004-08-17 05:23:38 +00002877 nanohist = histfilename();
Chris Allegretta1debce22003-02-13 22:00:19 +00002878
David Lawrence Ramseya27bd652004-08-17 05:23:38 +00002879 if (nanohist != NULL) {
2880 FILE *hist = fopen(nanohist, "wb");
2881
David Lawrence Ramsey36dd87b2004-07-18 17:43:43 +00002882 if (hist == NULL)
David Lawrence Ramsey6420d442004-08-11 05:13:08 +00002883 rcfile_error(N_("Error writing %s: %s"), nanohist, strerror(errno));
David Lawrence Ramsey36dd87b2004-07-18 17:43:43 +00002884 else {
Chris Allegrettaf3de8b52003-01-16 23:44:46 +00002885 /* set rw only by owner for security ?? */
2886 chmod(nanohist, S_IRUSR | S_IWUSR);
David Lawrence Ramseya27bd652004-08-17 05:23:38 +00002887
2888 if (!writehist(hist, &search_history) ||
2889 putc('\n', hist) == EOF ||
2890 !writehist(hist, &replace_history))
David Lawrence Ramsey1e7f1ea2004-08-12 02:11:35 +00002891 rcfile_error(N_("Error writing %s: %s"), nanohist, strerror(errno));
Chris Allegrettaf3de8b52003-01-16 23:44:46 +00002892 fclose(hist);
2893 }
2894 free(nanohist);
2895 }
2896}
David Lawrence Ramsey6420d442004-08-11 05:13:08 +00002897#endif /* !NANO_SMALL && ENABLE_NANORC */