blob: 44d651e8cd0c02e9adaa8d9166df667ce2cd947a [file] [log] [blame]
Chris Allegretta11b00112000-08-06 21:13:45 +00001/* $Id$ */
Chris Allegrettabceb1b22000-06-19 04:22:15 +00002/**************************************************************************
3 * files.c *
4 * *
5 * Copyright (C) 1999 Chris Allegretta *
6 * 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 *
8 * the Free Software Foundation; either version 1, or (at your option) *
9 * 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
22#include <stdlib.h>
23#include <string.h>
24#include <stdio.h>
25#include <unistd.h>
26#include <sys/types.h>
27#include <sys/stat.h>
28#include <fcntl.h>
29#include <errno.h>
30
31#include "config.h"
32#include "proto.h"
33#include "nano.h"
Chris Allegretta4da1fc62000-06-21 03:00:43 +000034
Chris Allegrettabceb1b22000-06-19 04:22:15 +000035#ifndef NANO_SMALL
36#include <libintl.h>
37#define _(string) gettext(string)
38#else
39#define _(string) (string)
40#endif
41
42/* Load file into edit buffer - takes data from file struct */
43void load_file(void)
44{
45 current = fileage;
46 wmove(edit, current_y, current_x);
47}
48
49/* What happens when there is no file to open? aiee! */
50void new_file(void)
51{
52 fileage = nmalloc(sizeof(filestruct));
53 fileage->data = nmalloc(1);
54 strcpy(fileage->data, "");
55 fileage->prev = NULL;
56 fileage->next = NULL;
57 fileage->lineno = 1;
58 filebot = fileage;
59 edittop = fileage;
60 editbot = fileage;
61 current = fileage;
62 totlines = 1;
63 UNSET(VIEW_MODE);
64}
65
66
67int read_byte(int fd, char *filename, char *input)
68{
69 static char buf[BUFSIZ];
70 static int index = 0;
71 static int size = 0;
72
73 if (index == size) {
74 index = 0;
75 size = read(fd, buf, BUFSIZ);
76 if (size == -1) {
77 clear();
78 refresh();
79 resetty();
80 endwin();
81 perror(filename);
82 }
83 if (!size)
84 return 0;
85 }
86 *input = buf[index++];
87 return 1;
88}
89
90filestruct *read_line(char *buf, filestruct * prev, int *line1ins)
91{
92 filestruct *fileptr;
93
94 fileptr = nmalloc(sizeof(filestruct));
95 fileptr->data = nmalloc(strlen(buf) + 2);
96 strcpy(fileptr->data, buf);
97
98 if (*line1ins) {
99 /* Special case, insert with cursor on 1st line. */
100 fileptr->prev = NULL;
101 fileptr->next = fileage;
102 fileptr->lineno = 1;
103 *line1ins = 0;
104 /* If we're inserting into the first line of the file, then
105 we want to make sure that our edit buffer stays on the
106 first line (and that fileage stays up to date!) */
107 fileage = fileptr;
108 edittop = fileptr;
109 } else if (fileage == NULL) {
110 fileage = fileptr;
111 fileage->lineno = 1;
112 fileage->next = fileage->prev = NULL;
113 fileptr = filebot = fileage;
114 } else if (prev) {
115 fileptr->prev = prev;
116 fileptr->next = NULL;
117 fileptr->lineno = prev->lineno + 1;
118 prev->next = fileptr;
119 } else {
120 die(_("read_line: not on first line and prev is NULL"));
121 }
122
123 return fileptr;
124}
125
126
127int read_file(int fd, char *filename)
128{
Adam Rogoyski1e328fb2000-07-08 03:57:16 +0000129 long size, num_lines = 0, linetemp = 0;
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000130 char input[2]; /* buffer */
131 char *buf;
132 long i = 0, bufx = 128;
133 filestruct *fileptr = current, *tmp = NULL;
134 int line1ins = 0;
135
136 buf = nmalloc(bufx);
Chris Allegretta8f6c0692000-07-19 01:16:18 +0000137 buf[0] = '\0';
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000138
139 if (fileptr != NULL && fileptr->prev != NULL) {
140 fileptr = fileptr->prev;
141 tmp = fileptr;
142 } else if (fileptr != NULL && fileptr->prev == NULL) {
143 tmp = fileage;
144 current = fileage;
145 line1ins = 1;
146 }
147 input[1] = 0;
148 /* Read the entire file into file struct */
149 while ((size = read_byte(fd, filename, input)) > 0) {
150 linetemp = 0;
151 if (input[0] == '\n') {
152 fileptr = read_line(buf, fileptr, &line1ins);
Adam Rogoyski1e328fb2000-07-08 03:57:16 +0000153 num_lines++;
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000154 buf[0] = 0;
155 i = 0;
156 } else {
157 /* Now we allocate a bigger buffer 128 characters at a time.
158 If we allocate a lot of space for one line, we may indeed
159 have to use a buffer this big later on, so we don't
160 decrease it at all. We do free it at the end though. */
161
162 if (i >= bufx - 1) {
163 buf = nrealloc(buf, bufx + 128);
164 bufx += 128;
165 }
166 buf[i] = input[0];
167 buf[i + 1] = 0;
168 i++;
169 }
170 totsize += size;
171 }
172
173 /* Did we not get a newline but still have stuff to do? */
174 if (buf[0]) {
175 fileptr = read_line(buf, fileptr, &line1ins);
Adam Rogoyski1e328fb2000-07-08 03:57:16 +0000176 num_lines++;
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000177 buf[0] = 0;
178 }
179 /* Did we even GET a file? */
180 if (totsize == 0) {
181 new_file();
Adam Rogoyski1e328fb2000-07-08 03:57:16 +0000182 statusbar(_("Read %d lines"), num_lines);
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000183 return 1;
184 }
Robert Siemborski63b3d7e2000-07-04 22:15:39 +0000185
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000186 if (current != NULL) {
187 fileptr->next = current;
188 current->prev = fileptr;
189 renumber(current);
190 current_x = 0;
191 placewewant = 0;
192 } else if (fileptr->next == NULL) {
193 filebot = fileptr;
Robert Siemborski63b3d7e2000-07-04 22:15:39 +0000194 new_magicline();
195
196 /* Update the edit buffer */
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000197 load_file();
198 }
Adam Rogoyski1e328fb2000-07-08 03:57:16 +0000199 statusbar(_("Read %d lines"), num_lines);
200 totlines += num_lines;
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000201
202 free(buf);
203 close(fd);
204
205 return 1;
206}
207
208/* Open the file (and decide if it exists) */
209int open_file(char *filename, int insert, int quiet)
210{
211 int fd;
212 struct stat fileinfo;
213
214 if (!strcmp(filename, "") || stat(filename, &fileinfo) == -1) {
215 if (insert) {
216 if (!quiet)
217 statusbar(_("\"%s\" not found"), filename);
218 return -1;
219 } else {
220 /* We have a new file */
221 statusbar(_("New File"));
222 new_file();
223 }
224 } else if ((fd = open(filename, O_RDONLY)) == -1) {
225 if (!quiet)
226 statusbar("%s: %s", strerror(errno), filename);
227 return -1;
228 } else { /* File is A-OK */
229 if (S_ISDIR(fileinfo.st_mode)) {
230 statusbar(_("File \"%s\" is a directory"), filename);
Chris Allegrettaf45c18d2000-09-16 05:25:06 +0000231 if (!insert)
232 new_file();
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000233 return -1;
234 }
235 if (!quiet)
236 statusbar(_("Reading File"));
237 read_file(fd, filename);
238 }
239
240 return 1;
241}
242
243int do_insertfile(void)
244{
245 int i;
246
247 wrap_reset();
248 i = statusq(writefile_list, WRITEFILE_LIST_LEN, "",
249 _("File to insert [from ./] "));
250 if (i != -1) {
251
252#ifdef DEBUG
253 fprintf(stderr, "filename is %s", answer);
254#endif
255
256 i = open_file(answer, 1, 0);
257
258 dump_buffer(fileage);
259 set_modified();
260
261 /* Here we want to rebuild the edit window */
Robert Siemborskidd53ec22000-07-04 02:35:19 +0000262 fix_editbot();
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000263
264 /* If we've gone off the bottom, recenter, otherwise just redraw */
Chris Allegretta4da1fc62000-06-21 03:00:43 +0000265 if (current->lineno > editbot->lineno)
Chris Allegretta234a34d2000-07-29 04:33:38 +0000266 edit_update(current, CENTER);
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000267 else
268 edit_refresh();
269
270 UNSET(KEEP_CUTBUFFER);
271 display_main_list();
272 return i;
273 } else {
274 statusbar(_("Cancelled"));
275 UNSET(KEEP_CUTBUFFER);
276 display_main_list();
277 return 0;
278 }
279}
280
281/*
282 * Write a file out. If tmp is nonzero, we set the umask to 0600,
283 * we don't set the global variable filename to it's name, and don't
284 * print out how many lines we wrote on the statusbar.
285 *
286 * Note that tmp is only set to 1 for storing temporary files internal
Chris Allegretta30885552000-07-14 01:20:12 +0000287 * to the editor, and is completely different from TEMP_OPT.
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000288 */
289int write_file(char *name, int tmp)
290{
291 long size, lineswritten = 0;
292 char buf[PATH_MAX + 1];
293 filestruct *fileptr;
294 int fd, mask = 0;
295 struct stat st;
296
297 if (!strcmp(name, "")) {
298 statusbar(_("Cancelled"));
299 return -1;
300 }
301 titlebar();
302 fileptr = fileage;
303
304
Chris Allegrettaacb62342000-07-21 22:42:46 +0000305 /* Check to see if the file is a regular file and FOLLOW_SYMLINKS is
306 set. If so then don't do the delete and recreate code which would
307 cause unexpected behavior */
308 lstat(name, &st);
309
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000310 /* Open the file and truncate it. Trust the symlink. */
Chris Allegrettaacb62342000-07-21 22:42:46 +0000311 if ((ISSET(FOLLOW_SYMLINKS) || !S_ISLNK(st.st_mode)) && !tmp) {
Chris Allegretta962c3c92000-07-24 21:52:17 +0000312
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000313 if ((fd = open(name, O_WRONLY | O_CREAT | O_TRUNC,
314 S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH |
315 S_IWOTH)) == -1) {
Chris Allegrettaa299b032000-07-14 02:44:02 +0000316 if (ISSET(TEMP_OPT)) {
317 UNSET(TEMP_OPT);
318 return do_writeout(1);
319 }
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000320 statusbar(_("Could not open file for writing: %s"),
321 strerror(errno));
322 return -1;
323 }
324 }
325 /* Don't follow symlink. Create new file. */
326 else {
327 if (strlen(name) > (PATH_MAX - 7)) {
328 statusbar(_("Could not open file: Path length exceeded."));
329 return -1;
330 }
331
332 memset(buf, 0x00, PATH_MAX + 1);
333 strcat(buf, name);
334 strcat(buf, ".XXXXXX");
335 if ((fd = mkstemp(buf)) == -1) {
Chris Allegrettaa299b032000-07-14 02:44:02 +0000336 if (ISSET(TEMP_OPT)) {
337 UNSET(TEMP_OPT);
338 return do_writeout(1);
Chris Allegrettabd9e7c32000-10-26 01:44:42 +0000339 }
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000340 statusbar(_("Could not open file for writing: %s"),
341 strerror(errno));
342 return -1;
343 }
344 }
345
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000346 dump_buffer(fileage);
347 while (fileptr != NULL && fileptr->next != NULL) {
Robert Siemborski63b3d7e2000-07-04 22:15:39 +0000348 /* Next line is so we discount the "magic line" */
Chris Allegrettabd9e7c32000-10-26 01:44:42 +0000349 if (filebot == fileptr && fileptr->data[0] == '\0')
350 break;
Robert Siemborski63b3d7e2000-07-04 22:15:39 +0000351
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000352 size = write(fd, fileptr->data, strlen(fileptr->data));
353 if (size == -1) {
354 statusbar(_("Could not open file for writing: %s"),
355 strerror(errno));
356 return -1;
357 } else {
358#ifdef DEBUG
359 fprintf(stderr, _("Wrote >%s\n"), fileptr->data);
360#endif
361 }
362 write(fd, "\n", 1);
363
364 fileptr = fileptr->next;
365 lineswritten++;
366 }
367
368 if (fileptr != NULL) {
369 size = write(fd, fileptr->data, strlen(fileptr->data));
370 if (size == -1) {
371 statusbar(_("Could not open file for writing: %s"),
372 strerror(errno));
373 return -1;
374 } else if (size > 0) {
375 size = write(fd, "\n", 1);
376 if (size == -1) {
377 statusbar(_("Could not open file for writing: %s"),
378 strerror(errno));
379 return -1;
380 }
381 }
382 }
383
384
385 if (close(fd) == -1) {
386 statusbar(_("Could not close %s: %s"), name, strerror(errno));
387 unlink(buf);
388 return -1;
389 }
390
391 if (!ISSET(FOLLOW_SYMLINKS) || tmp) {
392 if (stat(name, &st) == -1) {
393 /* Use default umask as file permisions if file is a new file. */
394 mask = umask(0);
395 umask(mask);
396
397 if (tmp) /* We don't want anyone reading our temporary file! */
398 mask = 0600;
399 else
400 mask = 0666 & ~mask;
401
402 } else {
403 /* Use permissions from file we are overwriting. */
404 mask = st.st_mode;
405 if (unlink(name) == -1) {
406 if (errno != ENOENT) {
407 statusbar(_("Could not open %s for writing: %s"),
408 name, strerror(errno));
409 unlink(buf);
410 return -1;
411 }
412 }
413 }
414
415 if (link(buf, name) != -1)
416 unlink(buf);
417 else if (errno != EPERM) {
418 statusbar(_("Could not open %s for writing: %s"),
419 name, strerror(errno));
420 unlink(buf);
421 return -1;
422 } else if (rename(buf, name) == -1) { /* Try a rename?? */
423 statusbar(_("Could not open %s for writing: %s"),
424 name, strerror(errno));
425 unlink(buf);
426 return -1;
427 }
428 if (chmod(name, mask) == -1) {
429 statusbar(_("Could not set permissions %o on %s: %s"),
430 mask, name, strerror(errno));
431 }
432
433 }
434 if (!tmp) {
435 strncpy(filename, name, 132);
436 statusbar(_("Wrote %d lines"), lineswritten);
437 }
438 UNSET(MODIFIED);
439 titlebar();
440 return 1;
441}
442
443int do_writeout(int exiting)
444{
445 int i = 0;
446
447 strncpy(answer, filename, 132);
448
Chris Allegretta962c3c92000-07-24 21:52:17 +0000449 if ((exiting) && (ISSET(TEMP_OPT))) {
Chris Allegrettabd9e7c32000-10-26 01:44:42 +0000450 if (filename[0]) {
Chris Allegretta962c3c92000-07-24 21:52:17 +0000451 i = write_file(answer, 0);
452 display_main_list();
453 return i;
Chris Allegrettabd9e7c32000-10-26 01:44:42 +0000454 } else {
Chris Allegretta461b2a92000-07-24 22:05:18 +0000455 UNSET(TEMP_OPT);
456 do_exit();
457
458 /* They cancelled, abort quit */
459 return -1;
Chris Allegretta962c3c92000-07-24 21:52:17 +0000460 }
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000461 }
462
463 while (1) {
464 i = statusq(writefile_list, WRITEFILE_LIST_LEN, answer,
Chris Allegretta4da1fc62000-06-21 03:00:43 +0000465 _("File Name to write"));
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000466
Chris Allegretta4da1fc62000-06-21 03:00:43 +0000467 if (i != -1) {
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000468
469#ifdef DEBUG
470 fprintf(stderr, _("filename is %s"), answer);
471#endif
472 if (strncmp(answer, filename, 132)) {
473 struct stat st;
474 if (!stat(answer, &st)) {
475 i = do_yesno(0, 0, _("File exists, OVERWRITE ?"));
476
477 if (!i || (i == -1))
478 continue;
Chris Allegretta4da1fc62000-06-21 03:00:43 +0000479 }
480 }
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000481 i = write_file(answer, 0);
482
483 display_main_list();
484 return i;
Chris Allegretta4da1fc62000-06-21 03:00:43 +0000485 } else {
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000486 statusbar(_("Cancelled"));
487 display_main_list();
488 return 0;
Chris Allegretta4da1fc62000-06-21 03:00:43 +0000489 }
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000490 }
491}
492
493int do_writeout_void(void)
494{
495 return do_writeout(0);
496}