blob: cc1fd4d5131400270000be4a93df05355c9b7aa3 [file] [log] [blame]
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001/**************************************************************************
2 * files.c *
3 * *
4 * Copyright (C) 1999 Chris Allegretta *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 1, or (at your option) *
8 * any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the Free Software *
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
18 * *
19 **************************************************************************/
20
21#include <stdlib.h>
22#include <string.h>
23#include <stdio.h>
24#include <unistd.h>
25#include <sys/types.h>
26#include <sys/stat.h>
27#include <fcntl.h>
28#include <errno.h>
29
30#include "config.h"
31#include "proto.h"
32#include "nano.h"
Chris Allegretta4da1fc62000-06-21 03:00:43 +000033
Chris Allegrettabceb1b22000-06-19 04:22:15 +000034#ifndef NANO_SMALL
35#include <libintl.h>
36#define _(string) gettext(string)
37#else
38#define _(string) (string)
39#endif
40
41/* Load file into edit buffer - takes data from file struct */
42void load_file(void)
43{
44 current = fileage;
45 wmove(edit, current_y, current_x);
46}
47
48/* What happens when there is no file to open? aiee! */
49void new_file(void)
50{
51 fileage = nmalloc(sizeof(filestruct));
52 fileage->data = nmalloc(1);
53 strcpy(fileage->data, "");
54 fileage->prev = NULL;
55 fileage->next = NULL;
56 fileage->lineno = 1;
57 filebot = fileage;
58 edittop = fileage;
59 editbot = fileage;
60 current = fileage;
61 totlines = 1;
62 UNSET(VIEW_MODE);
63}
64
65
66int read_byte(int fd, char *filename, char *input)
67{
68 static char buf[BUFSIZ];
69 static int index = 0;
70 static int size = 0;
71
72 if (index == size) {
73 index = 0;
74 size = read(fd, buf, BUFSIZ);
75 if (size == -1) {
76 clear();
77 refresh();
78 resetty();
79 endwin();
80 perror(filename);
81 }
82 if (!size)
83 return 0;
84 }
85 *input = buf[index++];
86 return 1;
87}
88
89filestruct *read_line(char *buf, filestruct * prev, int *line1ins)
90{
91 filestruct *fileptr;
92
93 fileptr = nmalloc(sizeof(filestruct));
94 fileptr->data = nmalloc(strlen(buf) + 2);
95 strcpy(fileptr->data, buf);
96
97 if (*line1ins) {
98 /* Special case, insert with cursor on 1st line. */
99 fileptr->prev = NULL;
100 fileptr->next = fileage;
101 fileptr->lineno = 1;
102 *line1ins = 0;
103 /* If we're inserting into the first line of the file, then
104 we want to make sure that our edit buffer stays on the
105 first line (and that fileage stays up to date!) */
106 fileage = fileptr;
107 edittop = fileptr;
108 } else if (fileage == NULL) {
109 fileage = fileptr;
110 fileage->lineno = 1;
111 fileage->next = fileage->prev = NULL;
112 fileptr = filebot = fileage;
113 } else if (prev) {
114 fileptr->prev = prev;
115 fileptr->next = NULL;
116 fileptr->lineno = prev->lineno + 1;
117 prev->next = fileptr;
118 } else {
119 die(_("read_line: not on first line and prev is NULL"));
120 }
121
122 return fileptr;
123}
124
125
126int read_file(int fd, char *filename)
127{
128 long size, lines = 0, linetemp = 0;
129 char input[2]; /* buffer */
130 char *buf;
131 long i = 0, bufx = 128;
132 filestruct *fileptr = current, *tmp = NULL;
133 int line1ins = 0;
134
135 buf = nmalloc(bufx);
136
137 if (fileptr != NULL && fileptr->prev != NULL) {
138 fileptr = fileptr->prev;
139 tmp = fileptr;
140 } else if (fileptr != NULL && fileptr->prev == NULL) {
141 tmp = fileage;
142 current = fileage;
143 line1ins = 1;
144 }
145 input[1] = 0;
146 /* Read the entire file into file struct */
147 while ((size = read_byte(fd, filename, input)) > 0) {
148 linetemp = 0;
149 if (input[0] == '\n') {
150 fileptr = read_line(buf, fileptr, &line1ins);
151 lines++;
152 buf[0] = 0;
153 i = 0;
154 } else {
155 /* Now we allocate a bigger buffer 128 characters at a time.
156 If we allocate a lot of space for one line, we may indeed
157 have to use a buffer this big later on, so we don't
158 decrease it at all. We do free it at the end though. */
159
160 if (i >= bufx - 1) {
161 buf = nrealloc(buf, bufx + 128);
162 bufx += 128;
163 }
164 buf[i] = input[0];
165 buf[i + 1] = 0;
166 i++;
167 }
168 totsize += size;
169 }
170
171 /* Did we not get a newline but still have stuff to do? */
172 if (buf[0]) {
173 fileptr = read_line(buf, fileptr, &line1ins);
174 lines++;
175 buf[0] = 0;
176 }
177 /* Did we even GET a file? */
178 if (totsize == 0) {
179 new_file();
180 statusbar(_("Read %d lines"), lines);
181 return 1;
182 }
183 if (current != NULL) {
184 fileptr->next = current;
185 current->prev = fileptr;
186 renumber(current);
187 current_x = 0;
188 placewewant = 0;
189 } else if (fileptr->next == NULL) {
190 filebot = fileptr;
191 load_file();
192 }
193 statusbar(_("Read %d lines"), lines);
194 totlines += lines;
195
196 free(buf);
197 close(fd);
198
199 return 1;
200}
201
202/* Open the file (and decide if it exists) */
203int open_file(char *filename, int insert, int quiet)
204{
205 int fd;
206 struct stat fileinfo;
207
208 if (!strcmp(filename, "") || stat(filename, &fileinfo) == -1) {
209 if (insert) {
210 if (!quiet)
211 statusbar(_("\"%s\" not found"), filename);
212 return -1;
213 } else {
214 /* We have a new file */
215 statusbar(_("New File"));
216 new_file();
217 }
218 } else if ((fd = open(filename, O_RDONLY)) == -1) {
219 if (!quiet)
220 statusbar("%s: %s", strerror(errno), filename);
221 return -1;
222 } else { /* File is A-OK */
223 if (S_ISDIR(fileinfo.st_mode)) {
224 statusbar(_("File \"%s\" is a directory"), filename);
225 new_file();
226 return -1;
227 }
228 if (!quiet)
229 statusbar(_("Reading File"));
230 read_file(fd, filename);
231 }
232
233 return 1;
234}
235
236int do_insertfile(void)
237{
238 int i;
239
240 wrap_reset();
241 i = statusq(writefile_list, WRITEFILE_LIST_LEN, "",
242 _("File to insert [from ./] "));
243 if (i != -1) {
244
245#ifdef DEBUG
246 fprintf(stderr, "filename is %s", answer);
247#endif
248
249 i = open_file(answer, 1, 0);
250
251 dump_buffer(fileage);
252 set_modified();
253
254 /* Here we want to rebuild the edit window */
Robert Siemborskidd53ec22000-07-04 02:35:19 +0000255 fix_editbot();
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000256
257 /* If we've gone off the bottom, recenter, otherwise just redraw */
Chris Allegretta4da1fc62000-06-21 03:00:43 +0000258 if (current->lineno > editbot->lineno)
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000259 edit_update(current);
260 else
261 edit_refresh();
262
263 UNSET(KEEP_CUTBUFFER);
264 display_main_list();
265 return i;
266 } else {
267 statusbar(_("Cancelled"));
268 UNSET(KEEP_CUTBUFFER);
269 display_main_list();
270 return 0;
271 }
272}
273
274/*
275 * Write a file out. If tmp is nonzero, we set the umask to 0600,
276 * we don't set the global variable filename to it's name, and don't
277 * print out how many lines we wrote on the statusbar.
278 *
279 * Note that tmp is only set to 1 for storing temporary files internal
280 * to the editor, and is completely different from temp_opt.
281 */
282int write_file(char *name, int tmp)
283{
284 long size, lineswritten = 0;
285 char buf[PATH_MAX + 1];
286 filestruct *fileptr;
287 int fd, mask = 0;
288 struct stat st;
289
290 if (!strcmp(name, "")) {
291 statusbar(_("Cancelled"));
292 return -1;
293 }
294 titlebar();
295 fileptr = fileage;
296
297
298 /* Open the file and truncate it. Trust the symlink. */
299 if (ISSET(FOLLOW_SYMLINKS) && !tmp) {
300 if ((fd = open(name, O_WRONLY | O_CREAT | O_TRUNC,
301 S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH |
302 S_IWOTH)) == -1) {
303 statusbar(_("Could not open file for writing: %s"),
304 strerror(errno));
305 return -1;
306 }
307 }
308 /* Don't follow symlink. Create new file. */
309 else {
310 if (strlen(name) > (PATH_MAX - 7)) {
311 statusbar(_("Could not open file: Path length exceeded."));
312 return -1;
313 }
314
315 memset(buf, 0x00, PATH_MAX + 1);
316 strcat(buf, name);
317 strcat(buf, ".XXXXXX");
318 if ((fd = mkstemp(buf)) == -1) {
319 statusbar(_("Could not open file for writing: %s"),
320 strerror(errno));
321 return -1;
322 }
323 }
324
325
326
327 dump_buffer(fileage);
328 while (fileptr != NULL && fileptr->next != NULL) {
329 size = write(fd, fileptr->data, strlen(fileptr->data));
330 if (size == -1) {
331 statusbar(_("Could not open file for writing: %s"),
332 strerror(errno));
333 return -1;
334 } else {
335#ifdef DEBUG
336 fprintf(stderr, _("Wrote >%s\n"), fileptr->data);
337#endif
338 }
339 write(fd, "\n", 1);
340
341 fileptr = fileptr->next;
342 lineswritten++;
343 }
344
345 if (fileptr != NULL) {
346 size = write(fd, fileptr->data, strlen(fileptr->data));
347 if (size == -1) {
348 statusbar(_("Could not open file for writing: %s"),
349 strerror(errno));
350 return -1;
351 } else if (size > 0) {
352 size = write(fd, "\n", 1);
353 if (size == -1) {
354 statusbar(_("Could not open file for writing: %s"),
355 strerror(errno));
356 return -1;
357 }
358 }
359 }
360
361
362 if (close(fd) == -1) {
363 statusbar(_("Could not close %s: %s"), name, strerror(errno));
364 unlink(buf);
365 return -1;
366 }
367
368 if (!ISSET(FOLLOW_SYMLINKS) || tmp) {
369 if (stat(name, &st) == -1) {
370 /* Use default umask as file permisions if file is a new file. */
371 mask = umask(0);
372 umask(mask);
373
374 if (tmp) /* We don't want anyone reading our temporary file! */
375 mask = 0600;
376 else
377 mask = 0666 & ~mask;
378
379 } else {
380 /* Use permissions from file we are overwriting. */
381 mask = st.st_mode;
382 if (unlink(name) == -1) {
383 if (errno != ENOENT) {
384 statusbar(_("Could not open %s for writing: %s"),
385 name, strerror(errno));
386 unlink(buf);
387 return -1;
388 }
389 }
390 }
391
392 if (link(buf, name) != -1)
393 unlink(buf);
394 else if (errno != EPERM) {
395 statusbar(_("Could not open %s for writing: %s"),
396 name, strerror(errno));
397 unlink(buf);
398 return -1;
399 } else if (rename(buf, name) == -1) { /* Try a rename?? */
400 statusbar(_("Could not open %s for writing: %s"),
401 name, strerror(errno));
402 unlink(buf);
403 return -1;
404 }
405 if (chmod(name, mask) == -1) {
406 statusbar(_("Could not set permissions %o on %s: %s"),
407 mask, name, strerror(errno));
408 }
409
410 }
411 if (!tmp) {
412 strncpy(filename, name, 132);
413 statusbar(_("Wrote %d lines"), lineswritten);
414 }
415 UNSET(MODIFIED);
416 titlebar();
417 return 1;
418}
419
420int do_writeout(int exiting)
421{
422 int i = 0;
423
424 strncpy(answer, filename, 132);
425
426 if ((exiting) && (temp_opt) && (filename)) {
427 i = write_file(answer, 0);
428 display_main_list();
429 return i;
430 }
431
432 while (1) {
433 i = statusq(writefile_list, WRITEFILE_LIST_LEN, answer,
Chris Allegretta4da1fc62000-06-21 03:00:43 +0000434 _("File Name to write"));
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000435
Chris Allegretta4da1fc62000-06-21 03:00:43 +0000436 if (i != -1) {
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000437
438#ifdef DEBUG
439 fprintf(stderr, _("filename is %s"), answer);
440#endif
441 if (strncmp(answer, filename, 132)) {
442 struct stat st;
443 if (!stat(answer, &st)) {
444 i = do_yesno(0, 0, _("File exists, OVERWRITE ?"));
445
446 if (!i || (i == -1))
447 continue;
Chris Allegretta4da1fc62000-06-21 03:00:43 +0000448 }
449 }
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000450 i = write_file(answer, 0);
451
452 display_main_list();
453 return i;
Chris Allegretta4da1fc62000-06-21 03:00:43 +0000454 } else {
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000455 statusbar(_("Cancelled"));
456 display_main_list();
457 return 0;
Chris Allegretta4da1fc62000-06-21 03:00:43 +0000458 }
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000459 }
460}
461
462int do_writeout_void(void)
463{
464 return do_writeout(0);
465}