blob: 03b68cbb3c703cee63fdd38f5d4d99cfeb5853c2 [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"
33
34#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 */
255 for(i = 0, editbot = edittop;
256 i <= editwinrows - 1
257 && i <= totlines
258 && editbot->next != NULL;
259 editbot = editbot->next, i++);
260
261 /* If we've gone off the bottom, recenter, otherwise just redraw */
262 if(current->lineno > editbot->lineno)
263 edit_update(current);
264 else
265 edit_refresh();
266
267 UNSET(KEEP_CUTBUFFER);
268 display_main_list();
269 return i;
270 } else {
271 statusbar(_("Cancelled"));
272 UNSET(KEEP_CUTBUFFER);
273 display_main_list();
274 return 0;
275 }
276}
277
278/*
279 * Write a file out. If tmp is nonzero, we set the umask to 0600,
280 * we don't set the global variable filename to it's name, and don't
281 * print out how many lines we wrote on the statusbar.
282 *
283 * Note that tmp is only set to 1 for storing temporary files internal
284 * to the editor, and is completely different from temp_opt.
285 */
286int write_file(char *name, int tmp)
287{
288 long size, lineswritten = 0;
289 char buf[PATH_MAX + 1];
290 filestruct *fileptr;
291 int fd, mask = 0;
292 struct stat st;
293
294 if (!strcmp(name, "")) {
295 statusbar(_("Cancelled"));
296 return -1;
297 }
298 titlebar();
299 fileptr = fileage;
300
301
302 /* Open the file and truncate it. Trust the symlink. */
303 if (ISSET(FOLLOW_SYMLINKS) && !tmp) {
304 if ((fd = open(name, O_WRONLY | O_CREAT | O_TRUNC,
305 S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH |
306 S_IWOTH)) == -1) {
307 statusbar(_("Could not open file for writing: %s"),
308 strerror(errno));
309 return -1;
310 }
311 }
312 /* Don't follow symlink. Create new file. */
313 else {
314 if (strlen(name) > (PATH_MAX - 7)) {
315 statusbar(_("Could not open file: Path length exceeded."));
316 return -1;
317 }
318
319 memset(buf, 0x00, PATH_MAX + 1);
320 strcat(buf, name);
321 strcat(buf, ".XXXXXX");
322 if ((fd = mkstemp(buf)) == -1) {
323 statusbar(_("Could not open file for writing: %s"),
324 strerror(errno));
325 return -1;
326 }
327 }
328
329
330
331 dump_buffer(fileage);
332 while (fileptr != NULL && fileptr->next != NULL) {
333 size = write(fd, fileptr->data, strlen(fileptr->data));
334 if (size == -1) {
335 statusbar(_("Could not open file for writing: %s"),
336 strerror(errno));
337 return -1;
338 } else {
339#ifdef DEBUG
340 fprintf(stderr, _("Wrote >%s\n"), fileptr->data);
341#endif
342 }
343 write(fd, "\n", 1);
344
345 fileptr = fileptr->next;
346 lineswritten++;
347 }
348
349 if (fileptr != NULL) {
350 size = write(fd, fileptr->data, strlen(fileptr->data));
351 if (size == -1) {
352 statusbar(_("Could not open file for writing: %s"),
353 strerror(errno));
354 return -1;
355 } else if (size > 0) {
356 size = write(fd, "\n", 1);
357 if (size == -1) {
358 statusbar(_("Could not open file for writing: %s"),
359 strerror(errno));
360 return -1;
361 }
362 }
363 }
364
365
366 if (close(fd) == -1) {
367 statusbar(_("Could not close %s: %s"), name, strerror(errno));
368 unlink(buf);
369 return -1;
370 }
371
372 if (!ISSET(FOLLOW_SYMLINKS) || tmp) {
373 if (stat(name, &st) == -1) {
374 /* Use default umask as file permisions if file is a new file. */
375 mask = umask(0);
376 umask(mask);
377
378 if (tmp) /* We don't want anyone reading our temporary file! */
379 mask = 0600;
380 else
381 mask = 0666 & ~mask;
382
383 } else {
384 /* Use permissions from file we are overwriting. */
385 mask = st.st_mode;
386 if (unlink(name) == -1) {
387 if (errno != ENOENT) {
388 statusbar(_("Could not open %s for writing: %s"),
389 name, strerror(errno));
390 unlink(buf);
391 return -1;
392 }
393 }
394 }
395
396 if (link(buf, name) != -1)
397 unlink(buf);
398 else if (errno != EPERM) {
399 statusbar(_("Could not open %s for writing: %s"),
400 name, strerror(errno));
401 unlink(buf);
402 return -1;
403 } else if (rename(buf, name) == -1) { /* Try a rename?? */
404 statusbar(_("Could not open %s for writing: %s"),
405 name, strerror(errno));
406 unlink(buf);
407 return -1;
408 }
409 if (chmod(name, mask) == -1) {
410 statusbar(_("Could not set permissions %o on %s: %s"),
411 mask, name, strerror(errno));
412 }
413
414 }
415 if (!tmp) {
416 strncpy(filename, name, 132);
417 statusbar(_("Wrote %d lines"), lineswritten);
418 }
419 UNSET(MODIFIED);
420 titlebar();
421 return 1;
422}
423
424int do_writeout(int exiting)
425{
426 int i = 0;
427
428 strncpy(answer, filename, 132);
429
430 if ((exiting) && (temp_opt) && (filename)) {
431 i = write_file(answer, 0);
432 display_main_list();
433 return i;
434 }
435
436 while (1) {
437 i = statusq(writefile_list, WRITEFILE_LIST_LEN, answer,
438 _("File Name to write"));
439
440 if (i != -1) {
441
442#ifdef DEBUG
443 fprintf(stderr, _("filename is %s"), answer);
444#endif
445 if (strncmp(answer, filename, 132)) {
446 struct stat st;
447 if (!stat(answer, &st)) {
448 i = do_yesno(0, 0, _("File exists, OVERWRITE ?"));
449
450 if (!i || (i == -1))
451 continue;
452 }
453 }
454 i = write_file(answer, 0);
455
456 display_main_list();
457 return i;
458 } else {
459 statusbar(_("Cancelled"));
460 display_main_list();
461 return 0;
462 }
463 }
464}
465
466int do_writeout_void(void)
467{
468 return do_writeout(0);
469}
470