blob: bd24dd3e32e5ef027f924037671f131c0b2f4a92 [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 }
Robert Siemborski63b3d7e2000-07-04 22:15:39 +0000183
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000184 if (current != NULL) {
185 fileptr->next = current;
186 current->prev = fileptr;
187 renumber(current);
188 current_x = 0;
189 placewewant = 0;
190 } else if (fileptr->next == NULL) {
191 filebot = fileptr;
Robert Siemborski63b3d7e2000-07-04 22:15:39 +0000192 new_magicline();
193
194 /* Update the edit buffer */
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000195 load_file();
196 }
197 statusbar(_("Read %d lines"), lines);
198 totlines += lines;
199
200 free(buf);
201 close(fd);
202
203 return 1;
204}
205
206/* Open the file (and decide if it exists) */
207int open_file(char *filename, int insert, int quiet)
208{
209 int fd;
210 struct stat fileinfo;
211
212 if (!strcmp(filename, "") || stat(filename, &fileinfo) == -1) {
213 if (insert) {
214 if (!quiet)
215 statusbar(_("\"%s\" not found"), filename);
216 return -1;
217 } else {
218 /* We have a new file */
219 statusbar(_("New File"));
220 new_file();
221 }
222 } else if ((fd = open(filename, O_RDONLY)) == -1) {
223 if (!quiet)
224 statusbar("%s: %s", strerror(errno), filename);
225 return -1;
226 } else { /* File is A-OK */
227 if (S_ISDIR(fileinfo.st_mode)) {
228 statusbar(_("File \"%s\" is a directory"), filename);
229 new_file();
230 return -1;
231 }
232 if (!quiet)
233 statusbar(_("Reading File"));
234 read_file(fd, filename);
235 }
236
237 return 1;
238}
239
240int do_insertfile(void)
241{
242 int i;
243
244 wrap_reset();
245 i = statusq(writefile_list, WRITEFILE_LIST_LEN, "",
246 _("File to insert [from ./] "));
247 if (i != -1) {
248
249#ifdef DEBUG
250 fprintf(stderr, "filename is %s", answer);
251#endif
252
253 i = open_file(answer, 1, 0);
254
255 dump_buffer(fileage);
256 set_modified();
257
258 /* Here we want to rebuild the edit window */
Robert Siemborskidd53ec22000-07-04 02:35:19 +0000259 fix_editbot();
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000260
261 /* If we've gone off the bottom, recenter, otherwise just redraw */
Chris Allegretta4da1fc62000-06-21 03:00:43 +0000262 if (current->lineno > editbot->lineno)
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000263 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) {
Robert Siemborski63b3d7e2000-07-04 22:15:39 +0000333 /* Next line is so we discount the "magic line" */
334 if(filebot == fileptr && fileptr->data[0] == '\0') break;
335
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000336 size = write(fd, fileptr->data, strlen(fileptr->data));
337 if (size == -1) {
338 statusbar(_("Could not open file for writing: %s"),
339 strerror(errno));
340 return -1;
341 } else {
342#ifdef DEBUG
343 fprintf(stderr, _("Wrote >%s\n"), fileptr->data);
344#endif
345 }
346 write(fd, "\n", 1);
347
348 fileptr = fileptr->next;
349 lineswritten++;
350 }
351
352 if (fileptr != NULL) {
353 size = write(fd, fileptr->data, strlen(fileptr->data));
354 if (size == -1) {
355 statusbar(_("Could not open file for writing: %s"),
356 strerror(errno));
357 return -1;
358 } else if (size > 0) {
359 size = write(fd, "\n", 1);
360 if (size == -1) {
361 statusbar(_("Could not open file for writing: %s"),
362 strerror(errno));
363 return -1;
364 }
365 }
366 }
367
368
369 if (close(fd) == -1) {
370 statusbar(_("Could not close %s: %s"), name, strerror(errno));
371 unlink(buf);
372 return -1;
373 }
374
375 if (!ISSET(FOLLOW_SYMLINKS) || tmp) {
376 if (stat(name, &st) == -1) {
377 /* Use default umask as file permisions if file is a new file. */
378 mask = umask(0);
379 umask(mask);
380
381 if (tmp) /* We don't want anyone reading our temporary file! */
382 mask = 0600;
383 else
384 mask = 0666 & ~mask;
385
386 } else {
387 /* Use permissions from file we are overwriting. */
388 mask = st.st_mode;
389 if (unlink(name) == -1) {
390 if (errno != ENOENT) {
391 statusbar(_("Could not open %s for writing: %s"),
392 name, strerror(errno));
393 unlink(buf);
394 return -1;
395 }
396 }
397 }
398
399 if (link(buf, name) != -1)
400 unlink(buf);
401 else if (errno != EPERM) {
402 statusbar(_("Could not open %s for writing: %s"),
403 name, strerror(errno));
404 unlink(buf);
405 return -1;
406 } else if (rename(buf, name) == -1) { /* Try a rename?? */
407 statusbar(_("Could not open %s for writing: %s"),
408 name, strerror(errno));
409 unlink(buf);
410 return -1;
411 }
412 if (chmod(name, mask) == -1) {
413 statusbar(_("Could not set permissions %o on %s: %s"),
414 mask, name, strerror(errno));
415 }
416
417 }
418 if (!tmp) {
419 strncpy(filename, name, 132);
420 statusbar(_("Wrote %d lines"), lineswritten);
421 }
422 UNSET(MODIFIED);
423 titlebar();
424 return 1;
425}
426
427int do_writeout(int exiting)
428{
429 int i = 0;
430
431 strncpy(answer, filename, 132);
432
433 if ((exiting) && (temp_opt) && (filename)) {
434 i = write_file(answer, 0);
435 display_main_list();
436 return i;
437 }
438
439 while (1) {
440 i = statusq(writefile_list, WRITEFILE_LIST_LEN, answer,
Chris Allegretta4da1fc62000-06-21 03:00:43 +0000441 _("File Name to write"));
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000442
Chris Allegretta4da1fc62000-06-21 03:00:43 +0000443 if (i != -1) {
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000444
445#ifdef DEBUG
446 fprintf(stderr, _("filename is %s"), answer);
447#endif
448 if (strncmp(answer, filename, 132)) {
449 struct stat st;
450 if (!stat(answer, &st)) {
451 i = do_yesno(0, 0, _("File exists, OVERWRITE ?"));
452
453 if (!i || (i == -1))
454 continue;
Chris Allegretta4da1fc62000-06-21 03:00:43 +0000455 }
456 }
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000457 i = write_file(answer, 0);
458
459 display_main_list();
460 return i;
Chris Allegretta4da1fc62000-06-21 03:00:43 +0000461 } else {
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000462 statusbar(_("Cancelled"));
463 display_main_list();
464 return 0;
Chris Allegretta4da1fc62000-06-21 03:00:43 +0000465 }
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000466 }
467}
468
469int do_writeout_void(void)
470{
471 return do_writeout(0);
472}