blob: 51536cd163fa44a1b70fe4208440093d479d231c [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
Chris Allegretta6efda542001-04-28 18:03:52 +000022#include "config.h"
23
Chris Allegrettabceb1b22000-06-19 04:22:15 +000024#include <stdlib.h>
25#include <string.h>
26#include <stdio.h>
27#include <unistd.h>
28#include <sys/types.h>
29#include <sys/stat.h>
30#include <fcntl.h>
31#include <errno.h>
Chris Allegretta04d848e2000-11-05 17:54:41 +000032#include <ctype.h>
33#include <dirent.h>
Chris Allegrettaee733e62001-01-22 22:17:08 +000034#include <pwd.h>
Chris Allegrettabceb1b22000-06-19 04:22:15 +000035
Chris Allegrettabceb1b22000-06-19 04:22:15 +000036#include "proto.h"
37#include "nano.h"
Chris Allegretta4da1fc62000-06-21 03:00:43 +000038
Chris Allegrettabceb1b22000-06-19 04:22:15 +000039#ifndef NANO_SMALL
40#include <libintl.h>
41#define _(string) gettext(string)
42#else
43#define _(string) (string)
44#endif
45
Chris Allegretta76e291b2001-10-14 19:05:10 +000046/* statics for here */
47#ifndef NANO_SMALL
48static int fileformat = 0; /* 0 = *nix, 1 = DOS, 2 = Mac */
49#endif
50
Chris Allegrettabceb1b22000-06-19 04:22:15 +000051/* Load file into edit buffer - takes data from file struct */
Chris Allegrettae1f14522001-09-19 03:19:43 +000052void load_file(int quiet)
Chris Allegrettabceb1b22000-06-19 04:22:15 +000053{
54 current = fileage;
Chris Allegretta2d7893d2001-07-11 02:08:33 +000055
Chris Allegretta355fbe52001-07-14 19:32:47 +000056#ifdef ENABLE_MULTIBUFFER
Chris Allegrettae1f14522001-09-19 03:19:43 +000057 /* if quiet is zero, add a new entry to the open_files structure, and
58 do duplicate checking; otherwise, update the current entry and
59 don't do duplicate checking (the latter is needed in the case of
60 the alternate spell checker); if a duplicate entry was found,
61 reload the currently open file (it may have been changed during
62 duplicate handling) */
63 if (quiet != 0)
64 quiet = 1;
65 if (add_open_file(quiet, 1 - quiet) == 2)
Chris Allegretta2d7893d2001-07-11 02:08:33 +000066 load_open_file();
67#endif
68
Chris Allegrettabceb1b22000-06-19 04:22:15 +000069 wmove(edit, current_y, current_x);
70}
71
72/* What happens when there is no file to open? aiee! */
73void new_file(void)
74{
75 fileage = nmalloc(sizeof(filestruct));
Chris Allegretta88b09152001-05-17 11:35:43 +000076 fileage->data = charalloc(1);
Chris Allegrettabceb1b22000-06-19 04:22:15 +000077 strcpy(fileage->data, "");
78 fileage->prev = NULL;
79 fileage->next = NULL;
80 fileage->lineno = 1;
81 filebot = fileage;
82 edittop = fileage;
83 editbot = fileage;
84 current = fileage;
85 totlines = 1;
Chris Allegretta1b3381b2001-09-28 21:59:01 +000086 totsize = 0;
Chris Allegrettae6421972001-07-18 01:03:36 +000087
88#ifdef ENABLE_MULTIBUFFER
89 /* if there aren't any entries in open_files, create the entry for
90 this new file, and, of course, don't bother checking for
91 duplicates; without this, if nano is started without a filename on
Chris Allegrettae1f14522001-09-19 03:19:43 +000092 the command line, a new file will be created, but it will be given
93 no open_files entry, leading to problems later on */
Chris Allegrettae6421972001-07-18 01:03:36 +000094 if (!open_files)
95 add_open_file(0, 0);
96#endif
97
Chris Allegrettabceb1b22000-06-19 04:22:15 +000098 UNSET(VIEW_MODE);
99}
100
101
102int read_byte(int fd, char *filename, char *input)
103{
104 static char buf[BUFSIZ];
105 static int index = 0;
106 static int size = 0;
107
108 if (index == size) {
109 index = 0;
110 size = read(fd, buf, BUFSIZ);
111 if (size == -1) {
112 clear();
113 refresh();
114 resetty();
115 endwin();
116 perror(filename);
117 }
118 if (!size)
119 return 0;
120 }
121 *input = buf[index++];
122 return 1;
123}
124
125filestruct *read_line(char *buf, filestruct * prev, int *line1ins)
126{
127 filestruct *fileptr;
128
129 fileptr = nmalloc(sizeof(filestruct));
Chris Allegretta88b09152001-05-17 11:35:43 +0000130 fileptr->data = charalloc(strlen(buf) + 2);
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000131 strcpy(fileptr->data, buf);
132
Chris Allegretta91841892001-09-21 02:37:01 +0000133#ifndef NANO_SMALL
Chris Allegretta03191762001-09-22 06:38:38 +0000134 /* If it's a DOS file (CRLF), strip out the CR part*/
Chris Allegretta91841892001-09-21 02:37:01 +0000135 if (buf[strlen(buf) - 1] == '\r') {
Chris Allegretta91841892001-09-21 02:37:01 +0000136 fileptr->data[strlen(buf) - 1] = 0;
Chris Allegretta7004c282001-09-22 00:42:10 +0000137 totsize--;
Chris Allegretta76e291b2001-10-14 19:05:10 +0000138
139 if (!fileformat)
140 fileformat = 1;
Chris Allegretta91841892001-09-21 02:37:01 +0000141 }
142#endif
143
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000144 if (*line1ins) {
145 /* Special case, insert with cursor on 1st line. */
146 fileptr->prev = NULL;
147 fileptr->next = fileage;
148 fileptr->lineno = 1;
149 *line1ins = 0;
150 /* If we're inserting into the first line of the file, then
151 we want to make sure that our edit buffer stays on the
152 first line (and that fileage stays up to date!) */
153 fileage = fileptr;
154 edittop = fileptr;
155 } else if (fileage == NULL) {
156 fileage = fileptr;
157 fileage->lineno = 1;
158 fileage->next = fileage->prev = NULL;
159 fileptr = filebot = fileage;
160 } else if (prev) {
161 fileptr->prev = prev;
162 fileptr->next = NULL;
163 fileptr->lineno = prev->lineno + 1;
164 prev->next = fileptr;
165 } else {
166 die(_("read_line: not on first line and prev is NULL"));
167 }
168
169 return fileptr;
170}
171
172
Chris Allegrettae1f14522001-09-19 03:19:43 +0000173int read_file(int fd, char *filename, int quiet)
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000174{
Adam Rogoyski1e328fb2000-07-08 03:57:16 +0000175 long size, num_lines = 0, linetemp = 0;
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000176 char input[2]; /* buffer */
177 char *buf;
178 long i = 0, bufx = 128;
179 filestruct *fileptr = current, *tmp = NULL;
180 int line1ins = 0;
181
Chris Allegretta88b09152001-05-17 11:35:43 +0000182 buf = charalloc(bufx);
Chris Allegretta8f6c0692000-07-19 01:16:18 +0000183 buf[0] = '\0';
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000184
185 if (fileptr != NULL && fileptr->prev != NULL) {
186 fileptr = fileptr->prev;
187 tmp = fileptr;
188 } else if (fileptr != NULL && fileptr->prev == NULL) {
189 tmp = fileage;
190 current = fileage;
191 line1ins = 1;
192 }
193 input[1] = 0;
194 /* Read the entire file into file struct */
195 while ((size = read_byte(fd, filename, input)) > 0) {
196 linetemp = 0;
Chris Allegretta03191762001-09-22 06:38:38 +0000197
198 if (input[0] == '\n') {
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000199 fileptr = read_line(buf, fileptr, &line1ins);
Adam Rogoyski1e328fb2000-07-08 03:57:16 +0000200 num_lines++;
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000201 buf[0] = 0;
202 i = 0;
Chris Allegretta03191762001-09-22 06:38:38 +0000203#ifndef NANO_SMALL
204 /* If it's a Mac file (no LF just a CR), handle it! */
205 } else if (i > 0 && buf[i-1] == '\r') {
Chris Allegretta76e291b2001-10-14 19:05:10 +0000206 fileformat = 2;
Chris Allegretta03191762001-09-22 06:38:38 +0000207 fileptr = read_line(buf, fileptr, &line1ins);
208 num_lines++;
209 buf[0] = input[0];
210 buf[1] = 0;
211 i = 1;
212#endif
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000213 } else {
214 /* Now we allocate a bigger buffer 128 characters at a time.
215 If we allocate a lot of space for one line, we may indeed
216 have to use a buffer this big later on, so we don't
Chris Allegretta88520c92001-05-05 17:45:54 +0000217 decrease it at all. We do free it at the end, though. */
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000218
219 if (i >= bufx - 1) {
220 buf = nrealloc(buf, bufx + 128);
221 bufx += 128;
222 }
223 buf[i] = input[0];
224 buf[i + 1] = 0;
225 i++;
226 }
227 totsize += size;
228 }
229
230 /* Did we not get a newline but still have stuff to do? */
231 if (buf[0]) {
232 fileptr = read_line(buf, fileptr, &line1ins);
Adam Rogoyski1e328fb2000-07-08 03:57:16 +0000233 num_lines++;
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000234 buf[0] = 0;
235 }
236 /* Did we even GET a file? */
Chris Allegretta9956e532000-11-26 02:09:53 +0000237 if (totsize == 0 || fileptr == NULL) {
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000238 new_file();
Adam Rogoyski1e328fb2000-07-08 03:57:16 +0000239 statusbar(_("Read %d lines"), num_lines);
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000240 return 1;
241 }
Robert Siemborski63b3d7e2000-07-04 22:15:39 +0000242
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000243 if (current != NULL) {
244 fileptr->next = current;
245 current->prev = fileptr;
246 renumber(current);
247 current_x = 0;
248 placewewant = 0;
249 } else if (fileptr->next == NULL) {
250 filebot = fileptr;
Robert Siemborski63b3d7e2000-07-04 22:15:39 +0000251 new_magicline();
Chris Allegretta321590a2000-12-10 06:03:40 +0000252 totsize--;
Robert Siemborski63b3d7e2000-07-04 22:15:39 +0000253
254 /* Update the edit buffer */
Chris Allegrettae1f14522001-09-19 03:19:43 +0000255 load_file(quiet);
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000256 }
Chris Allegretta76e291b2001-10-14 19:05:10 +0000257
258#ifndef NANO_SMALL
259 if (fileformat == 2)
260 statusbar(_("Read %d lines (Converted Mac format)"), num_lines);
261 else if (fileformat == 1)
262 statusbar(_("Read %d lines (Converted DOS format)"), num_lines);
263 else
264#endif
265 statusbar(_("Read %d lines"), num_lines);
266
Adam Rogoyski1e328fb2000-07-08 03:57:16 +0000267 totlines += num_lines;
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000268
269 free(buf);
270 close(fd);
271
272 return 1;
273}
274
275/* Open the file (and decide if it exists) */
276int open_file(char *filename, int insert, int quiet)
277{
278 int fd;
279 struct stat fileinfo;
280
281 if (!strcmp(filename, "") || stat(filename, &fileinfo) == -1) {
282 if (insert) {
283 if (!quiet)
284 statusbar(_("\"%s\" not found"), filename);
285 return -1;
286 } else {
287 /* We have a new file */
288 statusbar(_("New File"));
289 new_file();
290 }
291 } else if ((fd = open(filename, O_RDONLY)) == -1) {
292 if (!quiet)
293 statusbar("%s: %s", strerror(errno), filename);
Chris Allegretta3a7c0be2000-12-18 01:09:07 +0000294 if (!insert)
295 new_file();
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000296 return -1;
297 } else { /* File is A-OK */
Chris Allegretta7960dcf2000-12-13 15:01:29 +0000298 if (S_ISDIR(fileinfo.st_mode) || S_ISCHR(fileinfo.st_mode) ||
299 S_ISBLK(fileinfo.st_mode)) {
300 if (S_ISDIR(fileinfo.st_mode))
301 statusbar(_("File \"%s\" is a directory"), filename);
302 else
303 /* Don't open character or block files. Sorry, /dev/sndstat! */
304 statusbar(_("File \"%s\" is a device file"), filename);
305
Chris Allegrettae1f14522001-09-19 03:19:43 +0000306
Chris Allegrettaf45c18d2000-09-16 05:25:06 +0000307 if (!insert)
308 new_file();
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000309 return -1;
310 }
311 if (!quiet)
312 statusbar(_("Reading File"));
Chris Allegrettae1f14522001-09-19 03:19:43 +0000313 read_file(fd, filename, quiet);
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000314 }
315
316 return 1;
317}
318
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000319int do_insertfile(int loading_file)
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000320{
321 int i;
Chris Allegretta25f4e582000-11-25 05:03:20 +0000322 char *realname = NULL;
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000323
324 wrap_reset();
Chris Allegretta6fe61492001-05-21 12:56:25 +0000325
326#ifndef DISABLE_MOUSE
Chris Allegrettacc197ef2001-05-29 04:21:44 +0000327 currshortcut = insertfile_list;
328 currslen = INSERTFILE_LIST_LEN;
Chris Allegretta6fe61492001-05-21 12:56:25 +0000329#endif
330
Chris Allegrettacc197ef2001-05-29 04:21:44 +0000331 i = statusq(1, insertfile_list, INSERTFILE_LIST_LEN, "",
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000332 _("File to insert [from ./] "));
333 if (i != -1) {
334
335#ifdef DEBUG
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000336 fprintf(stderr, _("filename is %s"), answer);
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000337#endif
338
Rocco Corsi06aca1c2001-01-11 05:30:31 +0000339#ifndef DISABLE_TABCOMP
Chris Allegretta1bd0ce22000-12-06 05:56:08 +0000340 realname = real_dir_from_tilde(answer);
Chris Allegretta09a80842000-11-30 02:31:13 +0000341#else
Chris Allegretta1bd0ce22000-12-06 05:56:08 +0000342 realname = mallocstrcpy(realname, answer);
Chris Allegretta09a80842000-11-30 02:31:13 +0000343#endif
Chris Allegretta25f4e582000-11-25 05:03:20 +0000344
Rocco Corsiaf5c3022001-01-12 07:51:05 +0000345#ifndef DISABLE_BROWSER
Chris Allegrettaf4b96012001-01-03 07:11:47 +0000346 if (i == NANO_TOFILES_KEY) {
Chris Allegretta150469a2001-01-05 21:13:14 +0000347
348 char *tmp = do_browse_from(realname);
Chris Allegretta6fe61492001-05-21 12:56:25 +0000349#ifndef DISABLE_MOUSE
Chris Allegrettacc197ef2001-05-29 04:21:44 +0000350 currshortcut = insertfile_list;
351 currslen = INSERTFILE_LIST_LEN;
Chris Allegretta6fe61492001-05-21 12:56:25 +0000352#endif
Chris Allegrettaf4b96012001-01-03 07:11:47 +0000353
Rocco Corsi06aca1c2001-01-11 05:30:31 +0000354#ifdef DISABLE_TABCOMP
Chris Allegretta544347c2001-01-05 14:31:52 +0000355 realname = NULL;
Chris Allegretta123e5e62001-01-04 22:05:47 +0000356#endif
357 if (tmp != NULL)
Chris Allegretta544347c2001-01-05 14:31:52 +0000358 realname = mallocstrcpy(realname, tmp);
Chris Allegretta123e5e62001-01-04 22:05:47 +0000359 else
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000360 return do_insertfile(loading_file);
361 }
362#endif
363
Chris Allegrettae1f14522001-09-19 03:19:43 +0000364#ifndef DISABLE_OPERATINGDIR
365 if (operating_dir) {
366 if (check_operating_dir(realname, 0)) {
367 statusbar(_("Can't insert file from outside of %s"), operating_dir);
368 return 0;
369 }
370 }
371#endif
372
Chris Allegretta355fbe52001-07-14 19:32:47 +0000373#ifdef ENABLE_MULTIBUFFER
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000374 if (loading_file) {
375
376 /* update the current entry in the open_files structure; we
377 don't need to check for duplicate entries (the conditions
378 that could create them are taken care of elsewhere) */
379 add_open_file(1, 0);
380
381 free_filestruct(current);
382 new_file();
383 UNSET(MODIFIED);
Chris Allegrettaf4b96012001-01-03 07:11:47 +0000384 }
385#endif
386
Chris Allegretta25f4e582000-11-25 05:03:20 +0000387 i = open_file(realname, 1, 0);
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000388
Chris Allegretta355fbe52001-07-14 19:32:47 +0000389#ifdef ENABLE_MULTIBUFFER
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000390 if (loading_file)
391 filename = mallocstrcpy(filename, realname);
392#endif
393
Chris Allegretta25f4e582000-11-25 05:03:20 +0000394 free(realname);
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000395
396 dump_buffer(fileage);
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000397
Chris Allegretta355fbe52001-07-14 19:32:47 +0000398#ifdef ENABLE_MULTIBUFFER
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000399 if (loading_file)
Chris Allegrettae1f14522001-09-19 03:19:43 +0000400 load_file(0);
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000401 else
402#endif
403
404 set_modified();
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000405
406 /* Here we want to rebuild the edit window */
Robert Siemborskidd53ec22000-07-04 02:35:19 +0000407 fix_editbot();
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000408
Chris Allegretta355fbe52001-07-14 19:32:47 +0000409#ifdef ENABLE_MULTIBUFFER
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000410 /* If we've loaded another file, update the titlebar's contents */
411 if (loading_file) {
412 clearok(topwin, FALSE);
413 titlebar(NULL);
414
415 /* And re-init the shortcut list */
416 shortcut_init(0);
417 }
418#endif
419
Chris Allegretta88520c92001-05-05 17:45:54 +0000420 /* If we've gone off the bottom, recenter; otherwise, just redraw */
Chris Allegretta4da1fc62000-06-21 03:00:43 +0000421 if (current->lineno > editbot->lineno)
Chris Allegretta234a34d2000-07-29 04:33:38 +0000422 edit_update(current, CENTER);
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000423 else
424 edit_refresh();
425
426 UNSET(KEEP_CUTBUFFER);
427 display_main_list();
428 return i;
429 } else {
430 statusbar(_("Cancelled"));
431 UNSET(KEEP_CUTBUFFER);
432 display_main_list();
433 return 0;
434 }
435}
436
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000437int do_insertfile_void(void)
438{
439 int result = 0;
Chris Allegretta355fbe52001-07-14 19:32:47 +0000440#ifdef ENABLE_MULTIBUFFER
441 result = do_insertfile(ISSET(MULTIBUFFER));
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000442#else
443 result = do_insertfile(0);
444#endif
445
446 display_main_list();
447 return result;
448}
449
Chris Allegretta355fbe52001-07-14 19:32:47 +0000450#ifdef ENABLE_MULTIBUFFER
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000451/*
452 * Add/update an entry to the open_files filestruct. If update is
453 * zero, a new entry is created; otherwise, the current entry is updated.
454 * If dup_fix is zero, checking for and handling duplicate entries is not
455 * done; otherwise, it is. Return 0 on success, 1 on error, or 2 on
456 * finding a duplicate entry.
457 */
458int add_open_file(int update, int dup_fix)
459{
460 filestruct *tmp;
461
462 if (!current || !filename)
463 return 1;
464
465 /* first, if duplicate checking is allowed, do it */
466 if (dup_fix) {
467
468 /* if duplicates were found and handled, we're done */
469 if (open_file_dup_fix(update))
470 return 2;
471 }
472
473 /* if no entries, make the first one */
474 if (!open_files) {
475 open_files = make_new_node(NULL);
476
477 /* if open_files->file is NULL at the nrealloc() below, we get a
478 segfault */
479 open_files->file = open_files;
480 }
481
482 else if (!update) {
483
484 /* otherwise, if we're not updating, make a new entry for
485 open_files and splice it in after the current one */
486
487#ifdef DEBUG
488 fprintf(stderr, _("filename is %s"), open_files->data);
489#endif
490
491 tmp = make_new_node(NULL);
492 splice_node(open_files, tmp, open_files->next);
493 open_files = open_files->next;
494
495 /* if open_files->file is NULL at the nrealloc() below, we get a
496 segfault */
497 open_files->file = open_files;
498 }
499
500 /* save current filename */
501 open_files->data = mallocstrcpy(open_files->data, filename);
502
503 /* save the full path location */
504 open_files->file_path = get_full_path(open_files->data);
505
506 /* save current total number of lines */
507 open_files->file_totlines = totlines;
508
509 /* save current total size */
510 open_files->file_totsize = totsize;
511
512 /* save current x-coordinate position */
513 open_files->file_current_x = current_x;
514
515 /* save current y-coordinate position */
516 open_files->file_current_y = current_y;
517
518 /* save current place we want */
519 open_files->file_placewewant = placewewant;
520
521 /* save current line number */
522 open_files->lineno = current->lineno;
523
524 /* save current filestruct */
525 open_files->file = nrealloc(open_files->file, sizeof(filestruct));
526 while (current->prev)
527 current = current->prev;
528 open_files->file = copy_filestruct(current);
Chris Allegrettae1f14522001-09-19 03:19:43 +0000529 do_gotopos(open_files->lineno, open_files->file_current_x, open_files->file_current_y, open_files->file_placewewant);
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000530
531 /* save current modification status */
532 open_files->file_modified = ISSET(MODIFIED);
533
534#ifdef DEBUG
535 fprintf(stderr, _("filename is %s"), open_files->data);
536#endif
537
538 return 0;
539}
540
541/*
542 * Update only the filename and full path stored in the current entry.
543 * Return 0 on success or 1 on error.
544 */
545int open_file_change_name(void)
546{
547 if (!open_files || !filename)
548 return 1;
549
550 /* save current filename */
551 open_files->data = mallocstrcpy(open_files->data, filename);
552
553 /* save the full path location */
554 open_files->file_path = get_full_path(open_files->data);
555
556 return 0;
557}
558
559/*
560 * Read the current entry in the open_files structure and set up the
561 * currently open file using that entry's information. Return 0 on
562 * success or 1 on error.
563 */
564int load_open_file(void)
565{
566 if (!open_files)
567 return 1;
568
569 /* set up the filename, the file buffer, the total number of lines in
570 the file, and the total file size */
571 filename = mallocstrcpy(filename, open_files->data);
572 fileage = copy_filestruct(open_files->file);
573 current = fileage;
574 totlines = open_files->file_totlines;
575 totsize = open_files->file_totsize;
576
Chris Allegrettae1f14522001-09-19 03:19:43 +0000577 /* restore full file position: line number, x-coordinate, y-
578 coordinate, place we want */
579 do_gotopos(open_files->lineno, open_files->file_current_x, open_files->file_current_y, open_files->file_placewewant);
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000580
581 /* set up modification status and update the titlebar */
582 if (open_files->file_modified)
583 SET(MODIFIED);
584 else
585 UNSET(MODIFIED);
586 clearok(topwin, FALSE);
587 titlebar(NULL);
588
589 /* if we're constantly displaying the cursor position, update it */
590 if (ISSET(CONSTUPDATE))
591 do_cursorpos();
592
593 /* now we're done */
594 return 0;
595}
596
597/*
598 * Search the open_files structure for an entry with the same value for
599 * the file_path member as the current entry (i. e. a duplicate entry).
600 * If one is found, return a pointer to it; otherwise, return NULL.
601 *
602 * Note: This should only be called inside open_file_dup_fix().
603 */
Chris Allegrettae1f14522001-09-19 03:19:43 +0000604filestruct *open_file_dup_search(int update)
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000605{
606 filestruct *tmp;
607 char *path;
608
609 if (!open_files || !filename)
610 return NULL;
611
612 tmp = open_files;
613 path = get_full_path(filename);
614
615 /* if there's only one entry, handle it */
616 if (!tmp->prev && !tmp->next) {
617 if (!strcmp(tmp->file_path, path))
618 return tmp;
619 }
620
621 /* otherwise, go to the beginning */
622 while (tmp->prev)
623 tmp = tmp->prev;
624
625 /* and search the entries one by one */
626 while (tmp) {
627
628 if (!strcmp(tmp->file_path, path)) {
629
Chris Allegrettae1f14522001-09-19 03:19:43 +0000630 if (!update)
631 /* if we're making a new entry and there's an entry with
632 the same full path, we've found a duplicate */
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000633 return tmp;
Chris Allegrettae1f14522001-09-19 03:19:43 +0000634 else {
635
636 /* if we're updating an existing entry and there's an
637 entry with the same full path that isn't the current
638 entry, we've found a duplicate */
639 if (tmp != open_files)
640 return tmp;
641 }
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000642 }
643
644 /* go to the next entry */
645 tmp = tmp->next;
646
647 }
648
649 return NULL;
650}
651
652/*
653 * Search for duplicate entries in the open_files structure using
654 * open_file_dup_search(), and, if one is found, handle it properly.
655 * Return 0 if no duplicates were found, and 1 otherwise.
656 */
657int open_file_dup_fix(int update)
658{
Chris Allegrettae1f14522001-09-19 03:19:43 +0000659 filestruct *tmp = open_file_dup_search(update);
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000660
661 if (!tmp)
662 return 0;
663
664 /* if there's only one entry, handle it */
665 if (!tmp->prev && !tmp->next)
666 return 1;
667
668 /* otherwise, if we're not updating, the user's trying to load a
669 duplicate; switch to the original instead */
670 if (!update) {
671 open_files = tmp;
672 return 1;
673 }
674
675 /* if we are updating, the filename's been changed via a save; it's
676 thus more recent than the original, so remove the original */
677 else {
678 unlink_node(tmp);
679 free_filestruct(tmp->file);
680 free(tmp->file_path);
681 delete_node(tmp);
682 }
683 return 0;
684}
685
686/*
687 * Open the previous entry in the open_files structure. If closing_file
688 * is zero, update the current entry before switching from it.
689 * Otherwise, we are about to close that entry, so don't bother doing so.
690 * Return 0 on success and 1 on error.
691 */
692int open_prevfile(int closing_file)
693{
694 if (!open_files)
695 return 1;
696
697 /* if we're not about to close the current entry, update it before
698 doing anything; since we're only switching, we don't need to check
699 for duplicate entries */
700 if (!closing_file)
701 add_open_file(1, 0);
702
703 if (!open_files->prev && !open_files->next) {
704
705 /* only one file open */
706 if (!closing_file)
707 statusbar(_("No more open files"));
708 return 1;
709 }
710
711 if (open_files->prev) {
712 open_files = open_files->prev;
713
714#ifdef DEBUG
715 fprintf(stderr, _("filename is %s"), open_files->data);
716#endif
717
718 }
719
720 else if (open_files->next) {
721
722 /* if we're at the beginning, wrap around to the end */
723 while (open_files->next)
724 open_files = open_files->next;
725
726#ifdef DEBUG
727 fprintf(stderr, _("filename is %s"), open_files->data);
728#endif
729
730 }
731
732 load_open_file();
733
734#ifdef DEBUG
735 dump_buffer(current);
736#endif
737
738 return 0;
739}
740
741/*
742 * Open the next entry in the open_files structure. If closing_file is
743 * zero, update the current entry before switching from it. Otherwise, we
744 * are about to close that entry, so don't bother doing so. Return 0 on
745 * success and 1 on error.
746 */
747int open_nextfile(int closing_file)
748{
749 if (!open_files)
750 return 1;
751
752 /* if we're not about to close the current entry, update it before
753 doing anything; since we're only switching, we don't need to check
754 for duplicate entries */
755 if (!closing_file)
756 add_open_file(1, 0);
757
758 if (!open_files->prev && !open_files->next) {
759
760 /* only one file open */
761 if (!closing_file)
762 statusbar(_("No more open files"));
763 return 1;
764 }
765
766 if (open_files->next) {
767 open_files = open_files->next;
768
769#ifdef DEBUG
770 fprintf(stderr, _("filename is %s"), open_files->data);
771#endif
772
773 }
774 else if (open_files->prev) {
775
776 /* if we're at the end, wrap around to the beginning */
777 while (open_files->prev) {
778 open_files = open_files->prev;
779
780#ifdef DEBUG
781 fprintf(stderr, _("filename is %s"), open_files->data);
782#endif
783
784 }
785 }
786
787 load_open_file();
788
789#ifdef DEBUG
790 dump_buffer(current);
791#endif
792
793 return 0;
794}
795
796/*
797 * Delete an entry from the open_files filestruct. After deletion of an
Chris Allegrettae6421972001-07-18 01:03:36 +0000798 * entry, the next or previous entry is opened, whichever is found first.
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000799 * Return 0 on success or 1 on error.
800 */
801int close_open_file(void)
802{
803 filestruct *tmp;
804
805 if (!open_files)
806 return 1;
807
808 tmp = open_files;
Chris Allegrettae6421972001-07-18 01:03:36 +0000809 if (open_nextfile(1)) {
810 if (open_prevfile(1))
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000811 return 1;
812 }
813
814 unlink_node(tmp);
815 free_filestruct(tmp->file);
816 free(tmp->file_path);
817 delete_node(tmp);
818
819 shortcut_init(0);
820 display_main_list();
821 return 0;
822}
Chris Allegrettae1f14522001-09-19 03:19:43 +0000823#endif
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000824
Chris Allegrettae1f14522001-09-19 03:19:43 +0000825#if defined (ENABLE_MULTIBUFFER) || !defined (DISABLE_OPERATINGDIR)
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000826/*
Chris Allegrettae1f14522001-09-19 03:19:43 +0000827 * When passed "[relative path]" or "[relative path][filename]" in
828 * origpath, return "[full path]" or "[full path][filename]" on success,
829 * or NULL on error. This is still done if the file doesn't exist but
830 * the relative path does (since the file could exist in memory but not
831 * yet on disk); it is not done if the relative path doesn't exist (since
832 * the first call to chdir() will fail then).
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000833 */
Chris Allegrettae1f14522001-09-19 03:19:43 +0000834char *get_full_path(char *origpath)
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000835{
Chris Allegrettae1f14522001-09-19 03:19:43 +0000836 char *newpath = NULL, *last_slash, *d_here, *d_there, *d_there_file, tmp;
837 int path_only, last_slash_index;
838 struct stat fileinfo;
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000839
Chris Allegrettae1f14522001-09-19 03:19:43 +0000840 /* first, get the current directory, and tack a slash onto the end of
Chris Allegrettace78c1e2001-09-23 01:18:03 +0000841 it, unless it turns out to be "/", in which case leave it alone */
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000842
843#ifdef PATH_MAX
844 d_here = getcwd(NULL, PATH_MAX + 1);
845#else
846 d_here = getcwd(NULL, 0);
847#endif
848
849 if (d_here) {
850
851 align(&d_here);
Chris Allegrettace78c1e2001-09-23 01:18:03 +0000852 if (strcmp(d_here, "/")) {
853 d_here = nrealloc(d_here, strlen(d_here) + 2);
854 strcat(d_here, "/");
855 }
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000856
Chris Allegrettae1f14522001-09-19 03:19:43 +0000857 /* stat origpath; if stat() fails, assume that origpath refers to
858 a new file that hasn't been saved to disk yet (i. e. set
859 path_only to 0); if stat() succeeds, set path_only to 0 if
860 origpath doesn't refer to a directory, or to 1 if it does */
861 path_only = stat(origpath, &fileinfo);
862 if (path_only == -1)
863 path_only = 0;
864 else {
865 if (S_ISDIR(fileinfo.st_mode))
866 path_only = 1;
867 else
868 path_only = 0;
869 }
870
871 /* save the value of origpath in both d_there and d_there_file */
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000872 d_there = charalloc(strlen(origpath) + 1);
873 d_there_file = charalloc(strlen(origpath) + 1);
874 strcpy(d_there, origpath);
875 strcpy(d_there_file, origpath);
876
Chris Allegrettae1f14522001-09-19 03:19:43 +0000877 /* if we have a path but no filename, tack slashes onto the ends
878 of both d_there and d_there_file, if they don't end in slashes
879 already */
880 if (path_only) {
881 tmp = d_there[strlen(d_there) - 1];
882 if (tmp != '/') {
883 d_there = nrealloc(d_there, strlen(d_there) + 2);
884 strcat(d_there, "/");
885 d_there_file = nrealloc(d_there_file, strlen(d_there_file) + 2);
886 strcat(d_there_file, "/");
887 }
888 }
889
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000890 /* search for the last slash in d_there */
891 last_slash = strrchr(d_there, '/');
892
893 /* if we didn't find one, copy d_here into d_there; all data is
894 then set up */
895 if (!last_slash) {
896 d_there = nrealloc(d_there, strlen(d_here) + 1);
897 strcpy(d_there, d_here);
898 }
899
900 else {
901
Chris Allegrettae1f14522001-09-19 03:19:43 +0000902 /* otherwise, remove all non-path elements from d_there
903 (i. e. everything after the last slash) */
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000904 last_slash_index = strlen(d_there) - strlen(last_slash);
Chris Allegrettae1f14522001-09-19 03:19:43 +0000905 null_at(d_there, last_slash_index + 1);
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000906
Chris Allegrettae1f14522001-09-19 03:19:43 +0000907 /* and remove all non-file elements from d_there_file (i. e.
908 everything before and including the last slash); if we
909 have a path but no filename, don't do anything */
910 if (!path_only) {
911 last_slash = strrchr(d_there_file, '/');
912 last_slash++;
913 strcpy(d_there_file, last_slash);
914 align(&d_there_file);
915 }
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000916
917 /* now go to the path specified in d_there */
918 if (chdir(d_there) != -1) {
919
Chris Allegrettae1f14522001-09-19 03:19:43 +0000920 /* get the full pathname, and save it back in d_there,
921 tacking a slash on the end if we have a path but no
922 filename; if the saving fails, get out */
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000923
924 free(d_there);
925
926#ifdef PATH_MAX
927 d_there = getcwd(NULL, PATH_MAX + 1);
928#else
929 d_there = getcwd(NULL, 0);
930#endif
931
932 align(&d_there);
Chris Allegrettae1f14522001-09-19 03:19:43 +0000933 if (d_there) {
Chris Allegrettace78c1e2001-09-23 01:18:03 +0000934
935 /* add a slash to d_there, unless it's "/", in which
936 case we don't need it */
937 if (strcmp(d_there, "/")) {
938 d_there = nrealloc(d_there, strlen(d_there) + 2);
939 strcat(d_there, "/");
940 }
Chris Allegrettae1f14522001-09-19 03:19:43 +0000941 }
942 else
943 return NULL;
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000944 }
945
946 /* finally, go back to where we were before, d_here (no error
947 checking is done on this chdir(), because we can do
948 nothing if it fails) */
949 chdir(d_here);
950 }
951
952 /* all data is set up; fill in newpath */
953
Chris Allegrettae1f14522001-09-19 03:19:43 +0000954 /* if we have a path and a filename, newpath = d_there +
955 d_there_file; otherwise, newpath = d_there */
956 if (!path_only) {
957 newpath = charalloc(strlen(d_there) + strlen(d_there_file) + 1);
958 strcpy(newpath, d_there);
959 strcat(newpath, d_there_file);
960 }
961 else {
962 newpath = charalloc(strlen(d_there) + 1);
963 strcpy(newpath, d_there);
964 }
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000965
966 /* finally, clean up */
967 free(d_there_file);
968 free(d_there);
969 free(d_here);
970 }
971
972 return newpath;
973}
Chris Allegrettae1f14522001-09-19 03:19:43 +0000974#endif /* ENABLE_MULTIBUFFER || !DISABLE_OPERATINGDIR */
975
976#ifndef DISABLE_OPERATINGDIR
977/*
978 * Check to see if we're inside the operating directory. Return 0 if we
979 * are, or 1 otherwise. If allow_tabcomp is nonzero, allow incomplete
980 * names that would be matches for the operating directory, so that tab
981 * completion will work.
982 */
983int check_operating_dir(char *currpath, int allow_tabcomp)
984{
985 /* this is static so that we only need to get it the first time this
986 function is called; also, a relative operating directory path will
987 only be handled properly if this is done */
988 static char *full_operating_dir = NULL;
989
990 char *fullpath, *whereami1, *whereami2 = NULL;
991
992 /* if no operating directory is set, don't bother doing anything */
993 if (!operating_dir)
994 return 0;
995
996 /* if the operating directory is "/", that's the same as having no
997 operating directory, so discard it and get out */
998 if (!strcmp(operating_dir, "/")) {
999 operating_dir = NULL;
1000 return 0;
1001 }
1002
1003 /* get the full operating (if we don't have it already) and current
1004 directories, and then search the current for the operating (for
1005 normal usage) and the operating for the current (for tab
1006 completion, if we're allowing it); if the current directory's path
1007 doesn't exist, assume we're outside the operating directory */
1008 if (!full_operating_dir) {
1009 full_operating_dir = get_full_path(operating_dir);
1010
1011 /* if get_full_path() failed, discard the operating directory */
1012 if (!full_operating_dir) {
1013 operating_dir = NULL;
1014 return 0;
1015 }
Chris Allegrettace78c1e2001-09-23 01:18:03 +00001016
1017 /* if the full operating directory is "/", that's the same as
1018 having no operating directory, so discard it and get out */
1019 if (!strcmp(full_operating_dir, "/")) {
1020 free(full_operating_dir);
1021 operating_dir = NULL;
1022 return 0;
1023 }
Chris Allegrettae1f14522001-09-19 03:19:43 +00001024 }
1025
1026 fullpath = get_full_path(currpath);
1027 if (!fullpath)
1028 return 1;
1029
1030 whereami1 = strstr(fullpath, full_operating_dir);
1031 if (allow_tabcomp)
1032 whereami2 = strstr(full_operating_dir, fullpath);
1033
1034 /* if both searches failed, we're outside the operating directory */
1035 if (!whereami1 && !whereami2)
1036 return 1;
1037
1038 /* check the search results; if the full operating directory path is
1039 not at the beginning of the full current path (for normal usage)
1040 and vice versa (for tab completion, if we're allowing it), we're
1041 outside the operating directory */
1042 if (whereami1 != fullpath && whereami2 != full_operating_dir)
1043 return 1;
1044
1045 /* otherwise, we're still inside it */
1046 return 0;
1047}
Chris Allegretta2d7893d2001-07-11 02:08:33 +00001048#endif
1049
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001050/*
1051 * Write a file out. If tmp is nonzero, we set the umask to 0600,
Chris Allegretta88520c92001-05-05 17:45:54 +00001052 * we don't set the global variable filename to its name, and don't
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001053 * print out how many lines we wrote on the statusbar.
1054 *
Chris Allegretta49805172001-03-28 02:14:28 +00001055 * tmp means we are writing a tmp file in a secure fashion. We use
Chris Allegretta3dbb2782000-12-02 04:36:50 +00001056 * it when spell checking or dumping the file on an error.
Chris Allegrettacc197ef2001-05-29 04:21:44 +00001057 *
1058 * append means, not surprisingly, whether we are appending instead
1059 * of overwriting.
Chris Allegrettaecc3d7f2001-06-05 23:24:55 +00001060 *
1061 * nonamechange means don't change the current filename, it is ignored
1062 * if tmp == 1.
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001063 */
Chris Allegrettaecc3d7f2001-06-05 23:24:55 +00001064int write_file(char *name, int tmp, int append, int nonamechange)
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001065{
1066 long size, lineswritten = 0;
Chris Allegretta1a6e9042000-12-14 13:56:28 +00001067 static char *buf = NULL;
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001068 filestruct *fileptr;
Chris Allegretta07f9ee02000-12-04 05:15:39 +00001069 int fd, mask = 0, realexists, anyexists;
Chris Allegretta7960dcf2000-12-13 15:01:29 +00001070 struct stat st, lst;
Chris Allegretta0f5dfef2000-11-24 14:02:57 +00001071 static char *realname = NULL;
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001072
1073 if (!strcmp(name, "")) {
1074 statusbar(_("Cancelled"));
1075 return -1;
1076 }
Chris Allegrettaf4b96012001-01-03 07:11:47 +00001077 titlebar(NULL);
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001078 fileptr = fileage;
Chris Allegretta0f5dfef2000-11-24 14:02:57 +00001079
1080 if (realname != NULL)
1081 free(realname);
1082
Chris Allegretta1a6e9042000-12-14 13:56:28 +00001083 if (buf != NULL)
1084 free(buf);
1085
Rocco Corsi06aca1c2001-01-11 05:30:31 +00001086#ifndef DISABLE_TABCOMP
Chris Allegrettabe77c612000-11-24 14:00:16 +00001087 realname = real_dir_from_tilde(name);
1088#else
1089 realname = mallocstrcpy(realname, name);
1090#endif
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001091
Chris Allegrettae1f14522001-09-19 03:19:43 +00001092#ifndef DISABLE_OPERATINGDIR
1093 if (!tmp && operating_dir) {
1094 /* if we're writing a temporary file, we're going outside the
1095 operating directory, so skip the operating directory test */
1096 if (check_operating_dir(realname, 0)) {
1097 statusbar(_("Can't write outside of %s"), operating_dir);
1098
1099 return -1;
1100 }
1101 }
1102#endif
1103
Chris Allegretta1bd0ce22000-12-06 05:56:08 +00001104 /* Save the state of file at the end of the symlink (if there is one) */
Chris Allegrettaf7ee9e62000-12-02 21:13:50 +00001105 realexists = stat(realname, &st);
1106
Chris Allegretta1bd0ce22000-12-06 05:56:08 +00001107 /* Stat the link itself for the check... */
1108 anyexists = lstat(realname, &lst);
Chris Allegrettaacb62342000-07-21 22:42:46 +00001109
Chris Allegretta07f9ee02000-12-04 05:15:39 +00001110 /* New case: if the file exists, just give up */
1111 if (tmp && anyexists != -1)
Chris Allegretta1bd0ce22000-12-06 05:56:08 +00001112 return -1;
Chris Allegrettab5bb24c2000-12-06 00:57:54 +00001113 /* NOTE: If you change this statement, you MUST CHANGE the if
Chris Allegretta544d9b02000-12-11 02:47:13 +00001114 statement below (that says:
1115 if (realexists == -1 || tmp || (!ISSET(FOLLOW_SYMLINKS) &&
1116 S_ISLNK(lst.st_mode))) {
Chris Allegretta1bd0ce22000-12-06 05:56:08 +00001117 to reflect whether or not to link/unlink/rename the file */
1118 else if (ISSET(FOLLOW_SYMLINKS) || !S_ISLNK(lst.st_mode) || tmp) {
Chris Allegretta33084392000-12-09 22:50:38 +00001119 /* Use O_EXCL if tmp == 1. This is now copied from joe, because
Chris Allegrettaf5977772000-12-09 23:58:41 +00001120 wiggy says so *shrug*. */
Chris Allegrettacc197ef2001-05-29 04:21:44 +00001121 if (append)
1122 fd = open(realname, O_WRONLY | O_APPEND, (S_IRUSR|S_IWUSR));
1123 else if (tmp)
Chris Allegrettaf5977772000-12-09 23:58:41 +00001124 fd = open(realname, O_WRONLY | O_CREAT | O_EXCL, (S_IRUSR|S_IWUSR));
1125 else
1126 fd = open(realname, O_WRONLY | O_CREAT | O_TRUNC, (S_IRUSR|S_IWUSR));
Chris Allegretta07f9ee02000-12-04 05:15:39 +00001127
1128 /* First, just give up if we couldn't even open the file */
Chris Allegretta59828492000-12-04 03:31:39 +00001129 if (fd == -1) {
Chris Allegretta20c131c2000-12-04 04:20:09 +00001130 if (!tmp && ISSET(TEMP_OPT)) {
Chris Allegrettaa299b032000-07-14 02:44:02 +00001131 UNSET(TEMP_OPT);
Chris Allegrettacc197ef2001-05-29 04:21:44 +00001132 return do_writeout(filename, 1, 0);
Chris Allegrettaa299b032000-07-14 02:44:02 +00001133 }
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001134 statusbar(_("Could not open file for writing: %s"),
1135 strerror(errno));
1136 return -1;
1137 }
Chris Allegretta07f9ee02000-12-04 05:15:39 +00001138
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001139 }
1140 /* Don't follow symlink. Create new file. */
1141 else {
Chris Allegretta88b09152001-05-17 11:35:43 +00001142 buf = charalloc(strlen(realname) + 8);
Chris Allegretta1a6e9042000-12-14 13:56:28 +00001143 strncpy(buf, realname, strlen(realname)+1);
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001144 strcat(buf, ".XXXXXX");
1145 if ((fd = mkstemp(buf)) == -1) {
Chris Allegrettaa299b032000-07-14 02:44:02 +00001146 if (ISSET(TEMP_OPT)) {
1147 UNSET(TEMP_OPT);
Chris Allegrettacc197ef2001-05-29 04:21:44 +00001148 return do_writeout(filename, 1, 0);
Chris Allegrettabd9e7c32000-10-26 01:44:42 +00001149 }
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001150 statusbar(_("Could not open file for writing: %s"),
1151 strerror(errno));
1152 return -1;
1153 }
1154 }
1155
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001156 dump_buffer(fileage);
1157 while (fileptr != NULL && fileptr->next != NULL) {
Robert Siemborski63b3d7e2000-07-04 22:15:39 +00001158 /* Next line is so we discount the "magic line" */
Chris Allegrettabd9e7c32000-10-26 01:44:42 +00001159 if (filebot == fileptr && fileptr->data[0] == '\0')
1160 break;
Robert Siemborski63b3d7e2000-07-04 22:15:39 +00001161
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001162 size = write(fd, fileptr->data, strlen(fileptr->data));
1163 if (size == -1) {
1164 statusbar(_("Could not open file for writing: %s"),
1165 strerror(errno));
1166 return -1;
1167 } else {
1168#ifdef DEBUG
1169 fprintf(stderr, _("Wrote >%s\n"), fileptr->data);
1170#endif
1171 }
Chris Allegretta91841892001-09-21 02:37:01 +00001172#ifndef NANO_SMALL
Chris Allegretta8fa1e282001-09-22 04:20:25 +00001173 if (ISSET(DOS_FILE) || ISSET(MAC_FILE))
Chris Allegretta91841892001-09-21 02:37:01 +00001174 write(fd, "\r", 1);
Chris Allegretta8fa1e282001-09-22 04:20:25 +00001175
1176 if (!ISSET(MAC_FILE))
Chris Allegretta91841892001-09-21 02:37:01 +00001177#endif
Chris Allegretta8fa1e282001-09-22 04:20:25 +00001178 write(fd, "\n", 1);
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001179
1180 fileptr = fileptr->next;
1181 lineswritten++;
1182 }
1183
1184 if (fileptr != NULL) {
1185 size = write(fd, fileptr->data, strlen(fileptr->data));
1186 if (size == -1) {
1187 statusbar(_("Could not open file for writing: %s"),
1188 strerror(errno));
1189 return -1;
1190 } else if (size > 0) {
Chris Allegretta91841892001-09-21 02:37:01 +00001191#ifndef NANO_SMALL
Chris Allegretta960a8632001-09-22 05:08:12 +00001192 if (ISSET(DOS_FILE) || ISSET(MAC_FILE)) {
Chris Allegretta91841892001-09-21 02:37:01 +00001193 size = write(fd, "\r", 1);
1194 lineswritten++;
1195 if (size == -1) {
1196 statusbar(_("Could not open file for writing: %s"),
1197 strerror(errno));
1198 return -1;
1199 }
1200 }
Chris Allegretta8fa1e282001-09-22 04:20:25 +00001201
1202 if (!ISSET(MAC_FILE))
Chris Allegretta91841892001-09-21 02:37:01 +00001203#endif
Chris Allegretta8fa1e282001-09-22 04:20:25 +00001204 {
1205 size = write(fd, "\n", 1);
1206 lineswritten++;
1207 if (size == -1) {
1208 statusbar(_("Could not open file for writing: %s"),
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001209 strerror(errno));
Chris Allegretta8fa1e282001-09-22 04:20:25 +00001210 return -1;
1211 }
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001212 }
1213 }
1214 }
1215
1216
1217 if (close(fd) == -1) {
Chris Allegrettabe77c612000-11-24 14:00:16 +00001218 statusbar(_("Could not close %s: %s"), realname, strerror(errno));
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001219 unlink(buf);
1220 return -1;
1221 }
1222
Chris Allegretta1bd0ce22000-12-06 05:56:08 +00001223 if (realexists == -1 || tmp ||
1224 (!ISSET(FOLLOW_SYMLINKS) && S_ISLNK(lst.st_mode))) {
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001225
Chris Allegretta88520c92001-05-05 17:45:54 +00001226 /* Use default umask as file permissions if file is a new file. */
Chris Allegretta1bd0ce22000-12-06 05:56:08 +00001227 mask = umask(0);
1228 umask(mask);
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001229
Chris Allegretta1bd0ce22000-12-06 05:56:08 +00001230 if (tmp) /* We don't want anyone reading our temporary file! */
1231 mask = 0600;
1232 else
1233 mask = 0666 & ~mask;
1234 } else
Chris Allegrettada13f0b2000-12-06 05:59:31 +00001235 /* Use permissions from file we are overwriting. */
Chris Allegretta1bd0ce22000-12-06 05:56:08 +00001236 mask = st.st_mode;
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001237
Chris Allegretta1bd0ce22000-12-06 05:56:08 +00001238 if (!tmp && (!ISSET(FOLLOW_SYMLINKS) && S_ISLNK(lst.st_mode))) {
Chris Allegretta1bd0ce22000-12-06 05:56:08 +00001239 if (unlink(realname) == -1) {
1240 if (errno != ENOENT) {
Chris Allegrettaf7ee9e62000-12-02 21:13:50 +00001241 statusbar(_("Could not open %s for writing: %s"),
Chris Allegretta1bd0ce22000-12-06 05:56:08 +00001242 realname, strerror(errno));
Chris Allegrettaf7ee9e62000-12-02 21:13:50 +00001243 unlink(buf);
1244 return -1;
1245 }
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001246 }
Chris Allegretta1bd0ce22000-12-06 05:56:08 +00001247 if (link(buf, realname) != -1)
1248 unlink(buf);
1249 else if (errno != EPERM) {
1250 statusbar(_("Could not open %s for writing: %s"),
1251 name, strerror(errno));
1252 unlink(buf);
1253 return -1;
1254 } else if (rename(buf, realname) == -1) { /* Try a rename?? */
1255 statusbar(_("Could not open %s for writing: %s"),
1256 realname, strerror(errno));
1257 unlink(buf);
1258 return -1;
1259 }
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001260 }
Chris Allegretta1bd0ce22000-12-06 05:56:08 +00001261 if (chmod(realname, mask) == -1)
1262 statusbar(_("Could not set permissions %o on %s: %s"),
1263 mask, realname, strerror(errno));
1264
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001265 if (!tmp) {
Chris Allegretta0241d192001-06-05 23:45:54 +00001266 if (!nonamechange)
Chris Allegrettaecc3d7f2001-06-05 23:24:55 +00001267 filename = mallocstrcpy(filename, realname);
1268
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001269 statusbar(_("Wrote %d lines"), lineswritten);
Chris Allegretta1bd0ce22000-12-06 05:56:08 +00001270 UNSET(MODIFIED);
Chris Allegrettaf4b96012001-01-03 07:11:47 +00001271 titlebar(NULL);
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001272 }
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001273 return 1;
1274}
1275
Chris Allegrettacc197ef2001-05-29 04:21:44 +00001276int do_writeout(char *path, int exiting, int append)
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001277{
1278 int i = 0;
Chris Allegrettaf4b96012001-01-03 07:11:47 +00001279
Chris Allegretta8a0de3b2000-11-24 20:45:14 +00001280#ifdef NANO_EXTRA
1281 static int did_cred = 0;
1282#endif
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001283
Chris Allegretta6fe61492001-05-21 12:56:25 +00001284#ifndef DISABLE_MOUSE
Chris Allegretta6b58acd2001-04-12 03:01:53 +00001285 currshortcut = writefile_list;
1286 currslen = WRITEFILE_LIST_LEN;
Chris Allegretta6fe61492001-05-21 12:56:25 +00001287#endif
1288
Chris Allegrettae1ebaf32001-01-07 05:50:36 +00001289 answer = mallocstrcpy(answer, path);
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001290
Chris Allegretta962c3c92000-07-24 21:52:17 +00001291 if ((exiting) && (ISSET(TEMP_OPT))) {
Chris Allegrettabd9e7c32000-10-26 01:44:42 +00001292 if (filename[0]) {
Chris Allegrettaecc3d7f2001-06-05 23:24:55 +00001293 i = write_file(answer, 0, 0, 0);
Chris Allegretta962c3c92000-07-24 21:52:17 +00001294 display_main_list();
1295 return i;
Chris Allegrettabd9e7c32000-10-26 01:44:42 +00001296 } else {
Chris Allegretta461b2a92000-07-24 22:05:18 +00001297 UNSET(TEMP_OPT);
1298 do_exit();
1299
1300 /* They cancelled, abort quit */
1301 return -1;
Chris Allegretta962c3c92000-07-24 21:52:17 +00001302 }
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001303 }
1304
1305 while (1) {
Chris Allegretta9519eb02001-09-27 20:46:25 +00001306
1307 /* Be nice to the translation folks */
Chris Allegretta500b5e32001-06-21 23:58:47 +00001308#ifndef NANO_SMALL
Chris Allegretta9519eb02001-09-27 20:46:25 +00001309 if (ISSET(MARK_ISSET) && !exiting) {
1310 if (append)
1311 i = statusq(1, writefile_list, WRITEFILE_LIST_LEN, "",
1312 _("Append Selection to File"));
1313 else
1314 i = statusq(1, writefile_list, WRITEFILE_LIST_LEN, "",
1315 _("Write Selection to File"));
Chris Allegretta352801c2001-09-28 19:42:41 +00001316 } else
Chris Allegretta500b5e32001-06-21 23:58:47 +00001317#endif
Chris Allegretta352801c2001-09-28 19:42:41 +00001318 {
Chris Allegretta9519eb02001-09-27 20:46:25 +00001319 if (append)
1320 i = statusq(1, writefile_list, WRITEFILE_LIST_LEN, answer,
1321 _("File Name to Append"));
1322 else
1323 i = statusq(1, writefile_list, WRITEFILE_LIST_LEN, answer,
1324 _("File Name to Write"));
1325
1326 }
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001327
Chris Allegretta4da1fc62000-06-21 03:00:43 +00001328 if (i != -1) {
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001329
Rocco Corsiaf5c3022001-01-12 07:51:05 +00001330#ifndef DISABLE_BROWSER
Chris Allegrettaf4b96012001-01-03 07:11:47 +00001331 if (i == NANO_TOFILES_KEY) {
Chris Allegretta150469a2001-01-05 21:13:14 +00001332
1333 char *tmp = do_browse_from(answer);
Chris Allegretta6fe61492001-05-21 12:56:25 +00001334
1335#ifndef DISABLE_MOUSE
Chris Allegretta6b58acd2001-04-12 03:01:53 +00001336 currshortcut = writefile_list;
1337 currslen = WRITEFILE_LIST_LEN;
Chris Allegretta6fe61492001-05-21 12:56:25 +00001338#endif
Chris Allegrettaf4b96012001-01-03 07:11:47 +00001339
Chris Allegrettacc197ef2001-05-29 04:21:44 +00001340 if (tmp != NULL) {
Chris Allegretta544347c2001-01-05 14:31:52 +00001341 answer = mallocstrcpy(answer, tmp);
Chris Allegrettacc197ef2001-05-29 04:21:44 +00001342 } else
1343 return do_writeout(answer, exiting, append);
1344 } else
Chris Allegrettaf4b96012001-01-03 07:11:47 +00001345#endif
Chris Allegrettacc197ef2001-05-29 04:21:44 +00001346 if (i == NANO_APPEND_KEY)
1347 return(do_writeout(answer, exiting, 1 - append));
Chris Allegrettaf4b96012001-01-03 07:11:47 +00001348
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001349#ifdef DEBUG
1350 fprintf(stderr, _("filename is %s"), answer);
1351#endif
Chris Allegretta8a0de3b2000-11-24 20:45:14 +00001352
1353#ifdef NANO_EXTRA
Chris Allegretta1bd0ce22000-12-06 05:56:08 +00001354 if (exiting && !ISSET(TEMP_OPT) && !strcasecmp(answer, "zzy")
1355 && !did_cred) {
Chris Allegretta8a0de3b2000-11-24 20:45:14 +00001356 do_credits();
1357 did_cred = 1;
Chris Allegretta1bd0ce22000-12-06 05:56:08 +00001358 return -1;
Chris Allegretta8a0de3b2000-11-24 20:45:14 +00001359 }
1360#endif
Chris Allegrettacc197ef2001-05-29 04:21:44 +00001361 if (!append && strcmp(answer, filename)) {
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001362 struct stat st;
1363 if (!stat(answer, &st)) {
1364 i = do_yesno(0, 0, _("File exists, OVERWRITE ?"));
1365
1366 if (!i || (i == -1))
1367 continue;
Chris Allegretta4da1fc62000-06-21 03:00:43 +00001368 }
1369 }
Chris Allegrettacc197ef2001-05-29 04:21:44 +00001370#ifndef NANO_SMALL
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001371
Chris Allegrettacc197ef2001-05-29 04:21:44 +00001372 /* Here's where we allow the selected text to be written to
1373 a separate file. */
1374 if (ISSET(MARK_ISSET) && !exiting) {
Chris Allegrettacc197ef2001-05-29 04:21:44 +00001375 filestruct *fileagebak = fileage;
1376 filestruct *filebotbak = filebot;
1377 filestruct *cutback = cutbuffer;
1378 int oldmod = 0;
Chris Allegretta500b5e32001-06-21 23:58:47 +00001379 cutbuffer = NULL;
Chris Allegrettacc197ef2001-05-29 04:21:44 +00001380
1381 /* Okay, since write_file changes the filename, back it up */
Chris Allegrettacc197ef2001-05-29 04:21:44 +00001382 if (ISSET(MODIFIED))
1383 oldmod = 1;
1384
1385 /* Now, non-destructively add the marked text to the
1386 cutbuffer, and write the file out using the cutbuffer ;) */
Chris Allegretta500b5e32001-06-21 23:58:47 +00001387 if (current->lineno <= mark_beginbuf->lineno)
Chris Allegrettacc197ef2001-05-29 04:21:44 +00001388 cut_marked_segment(current, current_x, mark_beginbuf,
1389 mark_beginx, 0);
1390 else
1391 cut_marked_segment(mark_beginbuf, mark_beginx, current,
1392 current_x, 0);
1393
1394 fileage = cutbuffer;
1395 for (filebot = cutbuffer; filebot->next != NULL;
1396 filebot = filebot->next)
1397 ;
Chris Allegrettaecc3d7f2001-06-05 23:24:55 +00001398 i = write_file(answer, 0, append, 1);
Chris Allegrettacc197ef2001-05-29 04:21:44 +00001399
1400 /* Now restore everything */
Chris Allegrettacc197ef2001-05-29 04:21:44 +00001401 fileage = fileagebak;
1402 filebot = filebotbak;
1403 cutbuffer = cutback;
1404 if (oldmod)
1405 set_modified();
1406 } else
1407#endif
Chris Allegrettaecc3d7f2001-06-05 23:24:55 +00001408 i = write_file(answer, 0, append, 0);
Chris Allegretta2d7893d2001-07-11 02:08:33 +00001409
Chris Allegretta355fbe52001-07-14 19:32:47 +00001410#ifdef ENABLE_MULTIBUFFER
Chris Allegretta2d7893d2001-07-11 02:08:33 +00001411 /* if we're not about to exit, update the current entry in
1412 the open_files structure */
1413 if (!exiting) {
1414
1415 /* first, if the filename was changed during the save,
1416 update the filename and full path stored in the
1417 current entry, and then update the current entry,
1418 checking for duplicate entries */
Chris Allegrettae6421972001-07-18 01:03:36 +00001419 if (strcmp(open_files->data, filename)) {
Chris Allegretta2d7893d2001-07-11 02:08:33 +00001420 open_file_change_name();
1421 add_open_file(1, 1);
1422 }
1423 else {
1424
1425 /* otherwise, just update the current entry without
1426 checking for duplicate entries */
1427 add_open_file(1, 0);
1428 }
1429 }
1430#endif
1431
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001432 display_main_list();
1433 return i;
Chris Allegretta4da1fc62000-06-21 03:00:43 +00001434 } else {
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001435 statusbar(_("Cancelled"));
1436 display_main_list();
1437 return 0;
Chris Allegretta4da1fc62000-06-21 03:00:43 +00001438 }
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001439 }
1440}
1441
1442int do_writeout_void(void)
1443{
Chris Allegrettacc197ef2001-05-29 04:21:44 +00001444 return do_writeout(filename, 0, 0);
Chris Allegrettabceb1b22000-06-19 04:22:15 +00001445}
Chris Allegretta04d848e2000-11-05 17:54:41 +00001446
Rocco Corsi06aca1c2001-01-11 05:30:31 +00001447#ifndef DISABLE_TABCOMP
Chris Allegrettabe77c612000-11-24 14:00:16 +00001448
1449/* Return a malloc()ed string containing the actual directory, used
1450 * to convert ~user and ~/ notation...
1451 */
Chris Allegretta1bd0ce22000-12-06 05:56:08 +00001452char *real_dir_from_tilde(char *buf)
Chris Allegrettabe77c612000-11-24 14:00:16 +00001453{
Chris Allegrettae5e4d492001-01-23 03:09:15 +00001454 char *dirtmp = NULL, *find_user = NULL;
1455 int i = 1;
Chris Allegrettaee733e62001-01-22 22:17:08 +00001456 struct passwd *userdata;
Chris Allegrettabe77c612000-11-24 14:00:16 +00001457
Chris Allegrettae5e4d492001-01-23 03:09:15 +00001458 /* set a default value for dirtmp, in the case user home dir not found */
1459 dirtmp = mallocstrcpy(dirtmp, buf);
1460
Chris Allegrettabe77c612000-11-24 14:00:16 +00001461 if (buf[0] == '~') {
Chris Allegrettae5e4d492001-01-23 03:09:15 +00001462 if (buf[1] == 0 || buf[1] == '/') {
Chris Allegrettabe77c612000-11-24 14:00:16 +00001463 if (getenv("HOME") != NULL) {
Chris Allegrettae5e4d492001-01-23 03:09:15 +00001464
1465 free(dirtmp);
Chris Allegretta88b09152001-05-17 11:35:43 +00001466 dirtmp = charalloc(strlen(buf) + 2 + strlen(getenv("HOME")));
Chris Allegrettabe77c612000-11-24 14:00:16 +00001467
Chris Allegrettae5e4d492001-01-23 03:09:15 +00001468 sprintf(dirtmp, "%s%s", getenv("HOME"), &buf[1]);
Chris Allegrettabf692612001-01-08 16:59:19 +00001469
Chris Allegrettabe77c612000-11-24 14:00:16 +00001470 }
Chris Allegrettae5e4d492001-01-23 03:09:15 +00001471 }
1472 else {
Chris Allegrettabe77c612000-11-24 14:00:16 +00001473
Chris Allegretta88520c92001-05-05 17:45:54 +00001474 /* Figure how how much of the str we need to compare */
Chris Allegrettae5e4d492001-01-23 03:09:15 +00001475 for (i = 1; buf[i] != '/' && buf[i] != 0; i++)
1476 ;
1477
1478 find_user = mallocstrcpy(find_user, &buf[1]);
1479 find_user[i - 1] = 0;
Chris Allegretta04fec912000-11-25 04:43:43 +00001480
Chris Allegrettaee733e62001-01-22 22:17:08 +00001481 for (userdata = getpwent(); userdata != NULL &&
Chris Allegrettae5e4d492001-01-23 03:09:15 +00001482 strcmp(userdata->pw_name, find_user);
Chris Allegrettaee733e62001-01-22 22:17:08 +00001483 userdata = getpwent());
Chris Allegretta04fec912000-11-25 04:43:43 +00001484
Chris Allegrettae5e4d492001-01-23 03:09:15 +00001485 free(find_user);
Chris Allegretta04fec912000-11-25 04:43:43 +00001486
Chris Allegrettae5e4d492001-01-23 03:09:15 +00001487 if (userdata != NULL) { /* User found */
Chris Allegretta04fec912000-11-25 04:43:43 +00001488
Chris Allegrettae5e4d492001-01-23 03:09:15 +00001489 free(dirtmp);
Chris Allegretta88b09152001-05-17 11:35:43 +00001490 dirtmp = charalloc(strlen(buf) + 2 + strlen(userdata->pw_dir));
Chris Allegrettae5e4d492001-01-23 03:09:15 +00001491 sprintf(dirtmp, "%s%s", userdata->pw_dir, &buf[i]);
1492
1493 }
1494
Chris Allegrettaee733e62001-01-22 22:17:08 +00001495 endpwent();
Chris Allegrettabe77c612000-11-24 14:00:16 +00001496 }
Chris Allegrettae5e4d492001-01-23 03:09:15 +00001497 }
Chris Allegrettabe77c612000-11-24 14:00:16 +00001498
Chris Allegretta1bd0ce22000-12-06 05:56:08 +00001499 return dirtmp;
Chris Allegrettabe77c612000-11-24 14:00:16 +00001500}
1501
1502/* Tack a slash onto the string we're completing if it's a directory */
Chris Allegretta04fec912000-11-25 04:43:43 +00001503int append_slash_if_dir(char *buf, int *lastWasTab, int *place)
Chris Allegrettabe77c612000-11-24 14:00:16 +00001504{
1505 char *dirptr;
1506 struct stat fileinfo;
Chris Allegretta04fec912000-11-25 04:43:43 +00001507 int ret = 0;
Chris Allegrettabe77c612000-11-24 14:00:16 +00001508
1509 dirptr = real_dir_from_tilde(buf);
1510
1511 if (stat(dirptr, &fileinfo) == -1)
Chris Allegretta1bd0ce22000-12-06 05:56:08 +00001512 ret = 0;
Chris Allegrettabe77c612000-11-24 14:00:16 +00001513 else if (S_ISDIR(fileinfo.st_mode)) {
1514 strncat(buf, "/", 1);
1515 *place += 1;
1516 /* now we start over again with # of tabs so far */
1517 *lastWasTab = 0;
Chris Allegretta04fec912000-11-25 04:43:43 +00001518 ret = 1;
Chris Allegrettabe77c612000-11-24 14:00:16 +00001519 }
1520
1521 if (dirptr != buf)
Chris Allegretta1bd0ce22000-12-06 05:56:08 +00001522 free(dirptr);
Chris Allegretta04fec912000-11-25 04:43:43 +00001523
1524 return ret;
Chris Allegrettabe77c612000-11-24 14:00:16 +00001525}
Chris Allegretta04d848e2000-11-05 17:54:41 +00001526
1527/*
1528 * These functions (username_tab_completion, cwd_tab_completion, and
1529 * input_tab were taken from busybox 0.46 (cmdedit.c). Here is the notice
1530 * from that file:
1531 *
1532 * Termios command line History and Editting, originally
1533 * intended for NetBSD sh (ash)
1534 * Copyright (c) 1999
1535 * Main code: Adam Rogoyski <rogoyski@cs.utexas.edu>
1536 * Etc: Dave Cinege <dcinege@psychosis.com>
1537 * Majorly adjusted/re-written for busybox:
1538 * Erik Andersen <andersee@debian.org>
1539 *
1540 * You may use this code as you wish, so long as the original author(s)
1541 * are attributed in any redistributions of the source code.
1542 * This code is 'as is' with no warranty.
1543 * This code may safely be consumed by a BSD or GPL license.
1544 */
1545
1546char **username_tab_completion(char *buf, int *num_matches)
1547{
Chris Allegretta2c2c5f22001-01-23 03:27:31 +00001548 char **matches = (char **) NULL;
Chris Allegretta8d9b11a2001-01-19 04:17:24 +00001549 char *matchline = NULL;
Chris Allegretta2c2c5f22001-01-23 03:27:31 +00001550 struct passwd *userdata;
Chris Allegrettabe77c612000-11-24 14:00:16 +00001551
Chris Allegretta8d9b11a2001-01-19 04:17:24 +00001552 *num_matches = 0;
Rocco Corsi8b6cccc2001-01-02 06:21:07 +00001553 matches = nmalloc(BUFSIZ * sizeof(char *));
Chris Allegretta8d9b11a2001-01-19 04:17:24 +00001554
Chris Allegrettabe77c612000-11-24 14:00:16 +00001555 strcat(buf, "*");
Chris Allegretta8d9b11a2001-01-19 04:17:24 +00001556
Chris Allegretta2c2c5f22001-01-23 03:27:31 +00001557 while ((userdata = getpwent()) != NULL) {
Chris Allegrettabe77c612000-11-24 14:00:16 +00001558
Chris Allegretta2c2c5f22001-01-23 03:27:31 +00001559 if (check_wildcard_match(userdata->pw_name, &buf[1]) == TRUE) {
Chris Allegrettabe77c612000-11-24 14:00:16 +00001560
Chris Allegrettabe77c612000-11-24 14:00:16 +00001561 /* Cool, found a match. Add it to the list
1562 * This makes a lot more sense to me (Chris) this way...
1563 */
Chris Allegretta8d9b11a2001-01-19 04:17:24 +00001564
Chris Allegrettae1f14522001-09-19 03:19:43 +00001565#ifndef DISABLE_OPERATINGDIR
1566 /* ...unless the match exists outside the operating
1567 directory, in which case just go to the next match */
1568
1569 if (operating_dir) {
1570 if (check_operating_dir(userdata->pw_dir, 1))
1571 continue;
1572 }
1573#endif
1574
Chris Allegretta88b09152001-05-17 11:35:43 +00001575 matchline = charalloc(strlen(userdata->pw_name) + 2);
Chris Allegretta2c2c5f22001-01-23 03:27:31 +00001576 sprintf(matchline, "~%s", userdata->pw_name);
1577 matches[*num_matches] = matchline;
1578 ++*num_matches;
Chris Allegrettabe77c612000-11-24 14:00:16 +00001579
Chris Allegretta2c2c5f22001-01-23 03:27:31 +00001580 /* If there's no more room, bail out */
1581 if (*num_matches == BUFSIZ)
1582 break;
Chris Allegrettabe77c612000-11-24 14:00:16 +00001583 }
Chris Allegretta2c2c5f22001-01-23 03:27:31 +00001584 }
1585 endpwent();
Chris Allegretta8d9b11a2001-01-19 04:17:24 +00001586
Chris Allegretta04d848e2000-11-05 17:54:41 +00001587 return (matches);
1588}
1589
1590/* This was originally called exe_n_cwd_tab_completion, but we're not
1591 worried about executables, only filenames :> */
1592
1593char **cwd_tab_completion(char *buf, int *num_matches)
1594{
Chris Allegrettabe77c612000-11-24 14:00:16 +00001595 char *dirName, *dirtmp = NULL, *tmp = NULL, *tmp2 = NULL;
Chris Allegretta04d848e2000-11-05 17:54:41 +00001596 char **matches = (char **) NULL;
1597 DIR *dir;
1598 struct dirent *next;
1599
Rocco Corsi8b6cccc2001-01-02 06:21:07 +00001600 matches = nmalloc(BUFSIZ * sizeof(char *));
Chris Allegretta04d848e2000-11-05 17:54:41 +00001601
1602 /* Stick a wildcard onto the buf, for later use */
1603 strcat(buf, "*");
1604
Chris Allegretta2c975222000-11-15 01:25:42 +00001605 /* Okie, if there's a / in the buffer, strip out the directory part */
Chris Allegretta04d848e2000-11-05 17:54:41 +00001606 if (strcmp(buf, "") && strstr(buf, "/")) {
Chris Allegretta88b09152001-05-17 11:35:43 +00001607 dirName = charalloc(strlen(buf) + 1);
Chris Allegretta04d848e2000-11-05 17:54:41 +00001608 tmp = buf + strlen(buf);
1609 while (*tmp != '/' && tmp != buf)
Chris Allegrettab5b89ae2000-11-14 18:25:26 +00001610 tmp--;
Chris Allegretta442f2c52000-11-14 17:46:06 +00001611
Chris Allegretta04d848e2000-11-05 17:54:41 +00001612 tmp++;
1613
Chris Allegretta442f2c52000-11-14 17:46:06 +00001614 strncpy(dirName, buf, tmp - buf + 1);
1615 dirName[tmp - buf] = 0;
Chris Allegretta442f2c52000-11-14 17:46:06 +00001616
Chris Allegretta04d848e2000-11-05 17:54:41 +00001617 } else {
Chris Allegretta90d30742001-04-03 01:02:38 +00001618
1619#ifdef PATH_MAX
1620 if ((dirName = getcwd(NULL, PATH_MAX+1)) == NULL)
1621#else
Chris Allegretta88520c92001-05-05 17:45:54 +00001622 /* The better, but apparently segfault-causing way */
Chris Allegretta04d848e2000-11-05 17:54:41 +00001623 if ((dirName = getcwd(NULL, 0)) == NULL)
Chris Allegretta90d30742001-04-03 01:02:38 +00001624#endif /* PATH_MAX */
Chris Allegretta04d848e2000-11-05 17:54:41 +00001625 return matches;
1626 else
1627 tmp = buf;
1628 }
1629
1630#ifdef DEBUG
1631 fprintf(stderr, "\nDir = %s\n", dirName);
1632 fprintf(stderr, "\nbuf = %s\n", buf);
1633 fprintf(stderr, "\ntmp = %s\n", tmp);
1634#endif
Chris Allegrettab5b89ae2000-11-14 18:25:26 +00001635
Chris Allegrettabe77c612000-11-24 14:00:16 +00001636 dirtmp = real_dir_from_tilde(dirName);
1637 free(dirName);
1638 dirName = dirtmp;
1639
1640#ifdef DEBUG
1641 fprintf(stderr, "\nDir = %s\n", dirName);
1642 fprintf(stderr, "\nbuf = %s\n", buf);
1643 fprintf(stderr, "\ntmp = %s\n", tmp);
1644#endif
1645
1646
Chris Allegretta04d848e2000-11-05 17:54:41 +00001647 dir = opendir(dirName);
1648 if (!dir) {
1649 /* Don't print an error, just shut up and return */
1650 *num_matches = 0;
1651 beep();
1652 return (matches);
1653 }
1654 while ((next = readdir(dir)) != NULL) {
1655
Chris Allegretta04d848e2000-11-05 17:54:41 +00001656#ifdef DEBUG
Chris Allegretta2c975222000-11-15 01:25:42 +00001657 fprintf(stderr, "Comparing \'%s\'\n", next->d_name);
Chris Allegretta04d848e2000-11-05 17:54:41 +00001658#endif
1659 /* See if this matches */
1660 if (check_wildcard_match(next->d_name, tmp) == TRUE) {
Chris Allegretta7d97ce72000-11-06 04:04:15 +00001661
1662 /* Cool, found a match. Add it to the list
1663 * This makes a lot more sense to me (Chris) this way...
1664 */
Chris Allegrettae1f14522001-09-19 03:19:43 +00001665
1666#ifndef DISABLE_OPERATINGDIR
1667 /* ...unless the match exists outside the operating
1668 directory, in which case just go to the next match; to
1669 properly do operating directory checking, we have to add the
1670 directory name to the beginning of the proposed match
1671 before we check it */
1672
1673 if (operating_dir) {
1674 tmp2 = charalloc(strlen(dirName) + strlen(next->d_name) + 2);
1675 strcpy(tmp2, dirName);
1676 strcat(tmp2, "/");
1677 strcat(tmp2, next->d_name);
1678 if (check_operating_dir(tmp2, 1)) {
1679 free(tmp2);
1680 continue;
1681 }
1682 free(tmp2);
1683 }
1684#endif
1685
Chris Allegretta7d97ce72000-11-06 04:04:15 +00001686 tmp2 = NULL;
Chris Allegretta88b09152001-05-17 11:35:43 +00001687 tmp2 = charalloc(strlen(next->d_name) + 1);
Chris Allegretta7d97ce72000-11-06 04:04:15 +00001688 strcpy(tmp2, next->d_name);
1689 matches[*num_matches] = tmp2;
Chris Allegretta04d848e2000-11-05 17:54:41 +00001690 ++*num_matches;
Chris Allegretta2c975222000-11-15 01:25:42 +00001691
1692 /* If there's no more room, bail out */
1693 if (*num_matches == BUFSIZ)
1694 break;
Chris Allegretta04d848e2000-11-05 17:54:41 +00001695 }
1696 }
1697
1698 return (matches);
1699}
1700
Chris Allegretta442f2c52000-11-14 17:46:06 +00001701/* This function now has an arg which refers to how much the
Chris Allegretta04d848e2000-11-05 17:54:41 +00001702 * statusbar (place) should be advanced, i.e. the new cursor pos.
1703 */
Chris Allegrettab5b89ae2000-11-14 18:25:26 +00001704char *input_tab(char *buf, int place, int *lastWasTab, int *newplace)
Chris Allegretta04d848e2000-11-05 17:54:41 +00001705{
1706 /* Do TAB completion */
Chris Allegrettaec58a992000-11-05 21:54:23 +00001707 static int num_matches = 0, match_matches = 0;
Chris Allegretta04d848e2000-11-05 17:54:41 +00001708 static char **matches = (char **) NULL;
Chris Allegretta442f2c52000-11-14 17:46:06 +00001709 int pos = place, i = 0, col = 0, editline = 0;
Chris Allegretta04fec912000-11-25 04:43:43 +00001710 int longestname = 0, is_dir = 0;
Chris Allegretta3b0d1442000-11-05 21:56:54 +00001711 char *foo;
Chris Allegretta04d848e2000-11-05 17:54:41 +00001712
Chris Allegrettab5b89ae2000-11-14 18:25:26 +00001713 if (*lastWasTab == FALSE) {
Chris Allegretta442f2c52000-11-14 17:46:06 +00001714 char *tmp, *copyto, *matchBuf;
Chris Allegretta04d848e2000-11-05 17:54:41 +00001715
Chris Allegrettab5b89ae2000-11-14 18:25:26 +00001716 *lastWasTab = 1;
1717
Chris Allegretta04d848e2000-11-05 17:54:41 +00001718 /* Make a local copy of the string -- up to the position of the
1719 cursor */
Chris Allegrettaa35ec042001-01-15 02:26:12 +00001720 matchBuf = (char *) nmalloc((strlen(buf) + 2) * sizeof(char));
Chris Allegretta40587092001-01-16 04:45:38 +00001721 memset(matchBuf, '\0', (strlen(buf) + 2));
Chris Allegrettae118acc2000-11-06 05:40:03 +00001722
Chris Allegretta04d848e2000-11-05 17:54:41 +00001723 strncpy(matchBuf, buf, place);
1724 tmp = matchBuf;
1725
1726 /* skip any leading white space */
Chris Allegretta63a89d32000-11-18 03:05:50 +00001727 while (*tmp && isspace((int) *tmp))
Chris Allegretta04d848e2000-11-05 17:54:41 +00001728 ++tmp;
1729
1730 /* Free up any memory already allocated */
1731 if (matches != NULL) {
Chris Allegretta442f2c52000-11-14 17:46:06 +00001732 for (i = i; i < num_matches; i++)
1733 free(matches[i]);
Chris Allegretta04d848e2000-11-05 17:54:41 +00001734 free(matches);
1735 matches = (char **) NULL;
Chris Allegretta7d97ce72000-11-06 04:04:15 +00001736 num_matches = 0;
Chris Allegretta04d848e2000-11-05 17:54:41 +00001737 }
1738
1739 /* If the word starts with `~' and there is no slash in the word,
1740 * then try completing this word as a username. */
1741
Chris Allegrettabe77c612000-11-24 14:00:16 +00001742 /* FIXME -- this check is broken! */
Chris Allegretta1bd0ce22000-12-06 05:56:08 +00001743 if (*tmp == '~' && !strchr(tmp, '/'))
1744 matches = username_tab_completion(tmp, &num_matches);
Chris Allegretta04d848e2000-11-05 17:54:41 +00001745
1746 /* Try to match everything in the current working directory that
1747 * matches. */
1748 if (!matches)
1749 matches = cwd_tab_completion(tmp, &num_matches);
1750
1751 /* Don't leak memory */
1752 free(matchBuf);
1753
Chris Allegretta442f2c52000-11-14 17:46:06 +00001754#ifdef DEBUG
1755 fprintf(stderr, "%d matches found...\n", num_matches);
1756#endif
Chris Allegretta04d848e2000-11-05 17:54:41 +00001757 /* Did we find exactly one match? */
Chris Allegrettab5b89ae2000-11-14 18:25:26 +00001758 switch (num_matches) {
Chris Allegrettae118acc2000-11-06 05:40:03 +00001759 case 0:
Chris Allegretta24342432000-11-06 05:41:20 +00001760 blank_edit();
Chris Allegretta2c975222000-11-15 01:25:42 +00001761 wrefresh(edit);
Chris Allegrettae118acc2000-11-06 05:40:03 +00001762 break;
1763 case 1:
Chris Allegretta442f2c52000-11-14 17:46:06 +00001764
1765 buf = nrealloc(buf, strlen(buf) + strlen(matches[0]) + 1);
1766
1767 if (strcmp(buf, "") && strstr(buf, "/")) {
Chris Allegrettab5b89ae2000-11-14 18:25:26 +00001768 for (tmp = buf + strlen(buf); *tmp != '/' && tmp != buf;
1769 tmp--);
Chris Allegretta442f2c52000-11-14 17:46:06 +00001770 tmp++;
Chris Allegrettab5b89ae2000-11-14 18:25:26 +00001771 } else
Chris Allegretta442f2c52000-11-14 17:46:06 +00001772 tmp = buf;
1773
Chris Allegretta04fec912000-11-25 04:43:43 +00001774 if (!strcmp(tmp, matches[0]))
1775 is_dir = append_slash_if_dir(buf, lastWasTab, newplace);
1776
1777 if (is_dir)
1778 break;
Chris Allegretta442f2c52000-11-14 17:46:06 +00001779
1780 copyto = tmp;
Chris Allegrettab5b89ae2000-11-14 18:25:26 +00001781 for (pos = 0; *tmp == matches[0][pos] &&
1782 pos <= strlen(matches[0]); pos++)
Chris Allegretta442f2c52000-11-14 17:46:06 +00001783 tmp++;
1784
Chris Allegretta2c975222000-11-15 01:25:42 +00001785 /* write out the matched name */
Chris Allegrettab5b89ae2000-11-14 18:25:26 +00001786 strncpy(copyto, matches[0], strlen(matches[0]) + 1);
Chris Allegretta442f2c52000-11-14 17:46:06 +00001787 *newplace += strlen(matches[0]) - pos;
1788
Chris Allegrettae1f14522001-09-19 03:19:43 +00001789 /* if an exact match is typed in and Tab is pressed,
1790 *newplace will now be negative; in that case, make it
1791 zero, so that the cursor will stay where it is instead of
1792 moving backward */
1793 if (*newplace < 0)
1794 *newplace = 0;
1795
Chris Allegrettabe77c612000-11-24 14:00:16 +00001796 /* Is it a directory? */
1797 append_slash_if_dir(buf, lastWasTab, newplace);
1798
Chris Allegrettae118acc2000-11-06 05:40:03 +00001799 break;
1800 default:
Chris Allegretta88520c92001-05-05 17:45:54 +00001801 /* Check to see if all matches share a beginning, and, if so,
Chris Allegrettab5b89ae2000-11-14 18:25:26 +00001802 tack it onto buf and then beep */
Chris Allegrettaec58a992000-11-05 21:54:23 +00001803
Chris Allegretta442f2c52000-11-14 17:46:06 +00001804 if (strcmp(buf, "") && strstr(buf, "/")) {
Chris Allegrettab5b89ae2000-11-14 18:25:26 +00001805 for (tmp = buf + strlen(buf); *tmp != '/' && tmp != buf;
1806 tmp--);
Chris Allegretta442f2c52000-11-14 17:46:06 +00001807 tmp++;
Chris Allegrettab5b89ae2000-11-14 18:25:26 +00001808 } else
Chris Allegretta442f2c52000-11-14 17:46:06 +00001809 tmp = buf;
1810
1811 for (pos = 0; *tmp == matches[0][pos] && *tmp != 0 &&
Chris Allegrettab5b89ae2000-11-14 18:25:26 +00001812 pos <= strlen(matches[0]); pos++)
Chris Allegretta442f2c52000-11-14 17:46:06 +00001813 tmp++;
1814
Chris Allegrettaec58a992000-11-05 21:54:23 +00001815 while (1) {
1816 match_matches = 0;
1817
1818 for (i = 0; i < num_matches; i++) {
1819 if (matches[i][pos] == 0)
1820 break;
1821 else if (matches[i][pos] == matches[0][pos])
1822 match_matches++;
1823 }
Chris Allegrettab5b89ae2000-11-14 18:25:26 +00001824 if (match_matches == num_matches &&
1825 (i == num_matches || matches[i] != 0)) {
Chris Allegrettaec58a992000-11-05 21:54:23 +00001826 /* All the matches have the same character at pos+1,
Chris Allegrettab5b89ae2000-11-14 18:25:26 +00001827 so paste it into buf... */
Chris Allegretta75864952000-11-05 22:48:35 +00001828 buf = nrealloc(buf, strlen(buf) + 2);
Chris Allegretta442f2c52000-11-14 17:46:06 +00001829 strncat(buf, matches[0] + pos, 1);
Chris Allegrettab5b89ae2000-11-14 18:25:26 +00001830 *newplace += 1;
Chris Allegrettaec58a992000-11-05 21:54:23 +00001831 pos++;
Chris Allegretta75864952000-11-05 22:48:35 +00001832 } else {
Chris Allegrettaec58a992000-11-05 21:54:23 +00001833 beep();
1834 break;
1835 }
1836 }
Chris Allegrettae118acc2000-11-06 05:40:03 +00001837 break;
Chris Allegrettaec58a992000-11-05 21:54:23 +00001838 }
Chris Allegretta04d848e2000-11-05 17:54:41 +00001839 } else {
1840 /* Ok -- the last char was a TAB. Since they
1841 * just hit TAB again, print a list of all the
1842 * available choices... */
1843 if (matches && num_matches > 0) {
Chris Allegretta04d848e2000-11-05 17:54:41 +00001844
1845 /* Blank the edit window, and print the matches out there */
1846 blank_edit();
1847 wmove(edit, 0, 0);
1848
Chris Allegrettaec58a992000-11-05 21:54:23 +00001849 editline = 0;
Chris Allegretta2c975222000-11-15 01:25:42 +00001850
Chris Allegrettaec58a992000-11-05 21:54:23 +00001851 /* Figure out the length of the longest filename */
1852 for (i = 0; i < num_matches; i++)
1853 if (strlen(matches[i]) > longestname)
1854 longestname = strlen(matches[i]);
1855
1856 if (longestname > COLS - 1)
1857 longestname = COLS - 1;
1858
Chris Allegretta88b09152001-05-17 11:35:43 +00001859 foo = charalloc(longestname + 5);
Chris Allegretta3b0d1442000-11-05 21:56:54 +00001860
Chris Allegretta04d848e2000-11-05 17:54:41 +00001861 /* Print the list of matches */
1862 for (i = 0, col = 0; i < num_matches; i++) {
Chris Allegrettab5b89ae2000-11-14 18:25:26 +00001863
Chris Allegrettaec58a992000-11-05 21:54:23 +00001864 /* make each filename shown be the same length as the longest
Chris Allegrettab5b89ae2000-11-14 18:25:26 +00001865 filename, with two spaces at the end */
Chris Allegrettaec58a992000-11-05 21:54:23 +00001866 snprintf(foo, longestname + 1, matches[i]);
1867 while (strlen(foo) < longestname)
1868 strcat(foo, " ");
1869
1870 strcat(foo, " ");
1871
Chris Allegretta442f2c52000-11-14 17:46:06 +00001872 /* Disable el cursor */
1873 curs_set(0);
Chris Allegretta75864952000-11-05 22:48:35 +00001874 /* now, put the match on the screen */
1875 waddnstr(edit, foo, strlen(foo));
1876 col += strlen(foo);
1877
1878 /* And if the next match isn't going to fit on the
1879 line, move to the next one */
Chris Allegrettab5b89ae2000-11-14 18:25:26 +00001880 if (col > (COLS - longestname) && matches[i + 1] != NULL) {
Chris Allegrettaec58a992000-11-05 21:54:23 +00001881 editline++;
1882 wmove(edit, editline, 0);
Chris Allegrettab5b89ae2000-11-14 18:25:26 +00001883 if (editline == editwinrows - 1) {
1884 waddstr(edit, _("(more)"));
1885 break;
1886 }
Chris Allegretta04d848e2000-11-05 17:54:41 +00001887 col = 0;
1888 }
1889 }
Chris Allegretta3b0d1442000-11-05 21:56:54 +00001890 free(foo);
Chris Allegretta04d848e2000-11-05 17:54:41 +00001891 wrefresh(edit);
Chris Allegretta24dd8d62000-11-06 05:45:48 +00001892 } else
1893 beep();
Chris Allegretta04d848e2000-11-05 17:54:41 +00001894
1895 }
1896
1897 edit_refresh();
Chris Allegretta442f2c52000-11-14 17:46:06 +00001898 curs_set(1);
1899 return buf;
Chris Allegretta04d848e2000-11-05 17:54:41 +00001900}
Chris Allegrettabe77c612000-11-24 14:00:16 +00001901#endif
Chris Allegrettaf4b96012001-01-03 07:11:47 +00001902
Rocco Corsiaf5c3022001-01-12 07:51:05 +00001903#ifndef DISABLE_BROWSER
Chris Allegrettaf4b96012001-01-03 07:11:47 +00001904
1905/* Return the stat of the file pointed to by path */
1906struct stat filestat(const char *path) {
1907 struct stat st;
1908
Chris Allegretta0876dc92001-03-28 13:04:08 +00001909 stat(path, &st);
Chris Allegrettaf4b96012001-01-03 07:11:47 +00001910 return st;
1911}
1912
1913/* Our sort routine for file listings - sort directories before
1914 * files, and then alphabetically
1915 */
1916int diralphasort(const void *va, const void *vb) {
1917 struct stat file1info, file2info;
1918 char *a = *(char **)va, *b = *(char **)vb;
Chris Allegretta90d30742001-04-03 01:02:38 +00001919 int aisdir, bisdir;
Chris Allegrettaf4b96012001-01-03 07:11:47 +00001920
Chris Allegretta90d30742001-04-03 01:02:38 +00001921 aisdir = (stat(a, &file1info) != -1) && S_ISDIR(file1info.st_mode);
1922 bisdir = (stat(b, &file2info) != -1) && S_ISDIR(file2info.st_mode);
Chris Allegrettaf4b96012001-01-03 07:11:47 +00001923
Chris Allegretta90d30742001-04-03 01:02:38 +00001924 if (aisdir && !bisdir) return -1;
1925 if (!aisdir && bisdir) return 1;
Chris Allegrettaf4b96012001-01-03 07:11:47 +00001926
Chris Allegretta90d30742001-04-03 01:02:38 +00001927#ifdef HAVE_STRCASECMP
1928 return(strcasecmp(a,b));
1929#else
1930 return(strcmp(a,b));
1931#endif
1932
Chris Allegrettaf4b96012001-01-03 07:11:47 +00001933}
1934
Chris Allegretta0e9d3c62001-01-04 04:59:17 +00001935
1936/* Initialize the browser code, including the list of files in *path */
Chris Allegrettaf4b96012001-01-03 07:11:47 +00001937char **browser_init(char *path, int *longest, int *numents)
1938{
1939 DIR *dir;
1940 struct dirent *next;
1941 char **filelist = (char **) NULL;
1942 int i = 0;
1943
1944 dir = opendir(path);
1945 if (!dir)
1946 return NULL;
1947
1948 *numents = 0;
1949 while ((next = readdir(dir)) != NULL) {
1950 if (!strcmp(next->d_name, "."))
1951 continue;
1952 (*numents)++;
1953 if (strlen(next->d_name) > *longest)
1954 *longest = strlen(next->d_name);
1955 }
1956 rewinddir(dir);
1957 *longest += 10;
1958
1959 filelist = nmalloc(*numents * sizeof (char *));
1960
1961 while ((next = readdir(dir)) != NULL) {
1962 if (!strcmp(next->d_name, "."))
1963 continue;
Chris Allegretta88b09152001-05-17 11:35:43 +00001964 filelist[i] = charalloc(strlen(next->d_name) + strlen(path) + 2);
Chris Allegrettaf4b96012001-01-03 07:11:47 +00001965
1966 if (!strcmp(path, "/"))
1967 snprintf(filelist[i], strlen(next->d_name) + strlen(path) + 1,
1968 "%s%s", path, next->d_name);
1969 else
1970 snprintf(filelist[i], strlen(next->d_name) + strlen(path) + 2,
1971 "%s/%s", path, next->d_name);
Chris Allegrettaf4b96012001-01-03 07:11:47 +00001972 i++;
1973 }
1974
Chris Allegrettaf4b96012001-01-03 07:11:47 +00001975 if (*longest > COLS - 1)
1976 *longest = COLS - 1;
1977
1978 return filelist;
1979}
1980
Chris Allegretta88520c92001-05-05 17:45:54 +00001981/* Free our malloc()ed memory */
Chris Allegrettaf4b96012001-01-03 07:11:47 +00001982void free_charptrarray(char **array, int len)
1983{
1984 int i;
1985
1986 for (i = 0; i < len - 1; i++)
1987 free(array[i]);
1988 free(array);
1989}
1990
Chris Allegretta88520c92001-05-05 17:45:54 +00001991/* only print the last part of a path; isn't there a shell
Chris Allegretta0e9d3c62001-01-04 04:59:17 +00001992 command for this? */
Chris Allegrettaf4b96012001-01-03 07:11:47 +00001993char *tail(char *foo)
1994{
1995 char *tmp = NULL;
1996
1997 tmp = foo + strlen(foo);
1998 while (*tmp != '/' && tmp != foo)
1999 tmp--;
2000
2001 tmp++;
2002
2003 return tmp;
2004}
2005
Chris Allegretta0e9d3c62001-01-04 04:59:17 +00002006/* Strip one dir from the end of a string */
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002007void striponedir(char *foo)
2008{
2009 char *tmp = NULL;
2010
2011 /* Don't strip the root dir */
2012 if (!strcmp(foo, "/"))
2013 return;
2014
2015 tmp = foo + strlen(foo);
2016 if (*tmp == '/')
2017 tmp--;
2018
2019 while (*tmp != '/' && tmp != foo)
2020 tmp--;
2021
2022 if (tmp != foo)
2023 *tmp = 0;
2024 else
2025 *(tmp+1) = 0;
2026
2027 return;
2028}
2029
Chris Allegretta0e9d3c62001-01-04 04:59:17 +00002030/* Our browser function. inpath is the path to start browsing from */
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002031char *do_browser(char *inpath)
2032{
2033 struct stat st;
2034 char *foo, *retval = NULL;
2035 static char *path = NULL;
2036 int numents = 0, i = 0, j = 0, kbinput = 0, longest = 0, abort = 0;
2037 int col = 0, selected = 0, editline = 0, width = 0, filecols = 0;
Chris Allegrettac08f50d2001-01-06 18:12:43 +00002038 int lineno = 0, kb;
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002039 char **filelist = (char **) NULL;
Chris Allegretta051fc6e2001-05-05 23:17:36 +00002040#ifndef DISABLE_MOUSE
2041#ifdef NCURSES_MOUSE_VERSION
2042 MEVENT mevent;
2043#endif
2044#endif
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002045
Chris Allegretta0e9d3c62001-01-04 04:59:17 +00002046 /* If path isn't the same as inpath, we are being passed a new
2047 dir as an arg. We free it here so it will be copied from
2048 inpath below */
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002049 if (path != NULL && strcmp(path, inpath)) {
2050 free(path);
2051 path = NULL;
2052 }
2053
Chris Allegretta0e9d3c62001-01-04 04:59:17 +00002054 /* if path doesn't exist, make it so */
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002055 if (path == NULL)
2056 path = mallocstrcpy(path, inpath);
2057
2058 filelist = browser_init(path, &longest, &numents);
Chris Allegretta88b09152001-05-17 11:35:43 +00002059 foo = charalloc(longest + 8);
Chris Allegretta0e9d3c62001-01-04 04:59:17 +00002060
Chris Allegretta88520c92001-05-05 17:45:54 +00002061 /* Sort the list by directory first, then alphabetically */
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002062 qsort(filelist, numents, sizeof(char *), diralphasort);
2063
Chris Allegrettac08f50d2001-01-06 18:12:43 +00002064 kb = keypad_on(edit, 1);
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002065 titlebar(path);
2066 bottombars(browser_list, BROWSER_LIST_LEN);
2067 curs_set(0);
2068 wmove(edit, 0, 0);
2069 i = 0;
2070 width = 0;
2071 filecols = 0;
Chris Allegretta0e9d3c62001-01-04 04:59:17 +00002072
2073 /* Loop invariant: Microsoft sucks. */
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002074 do {
Rocco Corsi12f294c2001-04-14 06:50:24 +00002075 DIR *test_dir;
2076
2077 blank_statusbar_refresh();
2078
Chris Allegretta6fe61492001-05-21 12:56:25 +00002079#ifndef DISABLE_MOUSE
Chris Allegrettab10283c2001-05-07 12:08:37 +00002080 currshortcut = browser_list;
2081 currslen = BROWSER_LIST_LEN;
Chris Allegretta6fe61492001-05-21 12:56:25 +00002082#endif
2083
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002084 editline = 0;
2085 col = 0;
Chris Allegrettab0f52822001-01-04 05:20:23 +00002086
Chris Allegretta88520c92001-05-05 17:45:54 +00002087 /* Compute line number we're on now, so we don't divide by zero later */
Chris Allegrettab0f52822001-01-04 05:20:23 +00002088 if (width == 0)
2089 lineno = selected;
2090 else
2091 lineno = selected / width;
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002092
2093 switch (kbinput) {
Chris Allegretta6b58acd2001-04-12 03:01:53 +00002094
Chris Allegretta051fc6e2001-05-05 23:17:36 +00002095#ifndef DISABLE_MOUSE
Chris Allegretta6b58acd2001-04-12 03:01:53 +00002096#ifdef NCURSES_MOUSE_VERSION
2097 case KEY_MOUSE:
Chris Allegretta051fc6e2001-05-05 23:17:36 +00002098 if (getmouse(&mevent) == ERR)
2099 return retval;
2100
2101 /* If they clicked in the edit window, they probably clicked
2102 on a file */
2103 if (wenclose(edit, mevent.y, mevent.x)) {
2104 int selectedbackup = selected;
2105
2106 mevent.y -= 2;
2107
2108 /* If we're on line 0, don't toy with finding out what
2109 page we're on */
2110 if (lineno / editwinrows == 0)
2111 selected = mevent.y * width + mevent.x / longest;
2112 else
2113 selected = (lineno / editwinrows) * editwinrows * width
2114 + mevent.y * width + mevent.x / longest;
2115
2116 /* If we're off the screen, reset to the last item.
2117 If we clicked where we did last time, select this name! */
Chris Allegrettaf3fde7c2001-05-06 03:02:21 +00002118 if (selected > numents - 1)
Chris Allegretta051fc6e2001-05-05 23:17:36 +00002119 selected = numents - 1;
2120 else if (selectedbackup == selected) {
2121 ungetch('s'); /* Unget the 'select' key */
2122 break;
2123 }
2124 } else /* Must be clicking a shortcut */
2125 do_mouse();
2126
Chris Allegretta6b58acd2001-04-12 03:01:53 +00002127 break;
2128#endif
2129#endif
Chris Allegrettaa2c02e92001-07-02 01:31:44 +00002130 case NANO_UP_KEY:
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002131 case KEY_UP:
2132 case 'u':
2133 if (selected - width >= 0)
2134 selected -= width;
2135 break;
Chris Allegrettaa2c02e92001-07-02 01:31:44 +00002136 case NANO_BACK_KEY:
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002137 case KEY_LEFT:
Chris Allegrettaa2c02e92001-07-02 01:31:44 +00002138 case NANO_BACKSPACE_KEY:
2139 case 127:
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002140 case 'l':
2141 if (selected > 0)
2142 selected--;
2143 break;
2144 case KEY_DOWN:
Chris Allegrettaa2c02e92001-07-02 01:31:44 +00002145 case NANO_DOWN_KEY:
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002146 case 'd':
2147 if (selected + width <= numents - 1)
2148 selected += width;
2149 break;
2150 case KEY_RIGHT:
Chris Allegrettaa2c02e92001-07-02 01:31:44 +00002151 case NANO_FORWARD_KEY:
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002152 case 'r':
2153 if (selected < numents - 1)
2154 selected++;
2155 break;
2156 case NANO_PREVPAGE_KEY:
Chris Allegretta6e827412001-01-04 04:41:08 +00002157 case NANO_PREVPAGE_FKEY:
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002158 case KEY_PPAGE:
Chris Allegretta8eac3b52001-01-08 21:14:04 +00002159 case '-':
Chris Allegrettab0f52822001-01-04 05:20:23 +00002160
2161 if (lineno % editwinrows == 0) {
Chris Allegretta425a2662001-01-03 15:09:27 +00002162 if (selected - (editwinrows * width) >= 0)
2163 selected -= editwinrows * width;
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002164 else
2165 selected = 0;
2166 }
2167 else if (selected - (editwinrows +
Chris Allegrettab0f52822001-01-04 05:20:23 +00002168 lineno % editwinrows) * width >= 0)
2169
2170 selected -= (editwinrows + lineno % editwinrows) * width;
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002171 else
2172 selected = 0;
2173 break;
2174 case NANO_NEXTPAGE_KEY:
Chris Allegretta6e827412001-01-04 04:41:08 +00002175 case NANO_NEXTPAGE_FKEY:
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002176 case KEY_NPAGE:
Chris Allegretta8eac3b52001-01-08 21:14:04 +00002177 case ' ':
Chris Allegrettab0f52822001-01-04 05:20:23 +00002178 if (lineno % editwinrows == 0) {
Chris Allegretta425a2662001-01-03 15:09:27 +00002179 if (selected + (editwinrows * width) <= numents - 1)
2180 selected += editwinrows * width;
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002181 else
2182 selected = numents - 1;
2183 }
2184 else if (selected + (editwinrows -
Chris Allegrettab0f52822001-01-04 05:20:23 +00002185 lineno % editwinrows) * width <= numents - 1)
2186 selected += (editwinrows - lineno % editwinrows) * width;
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002187 else
2188 selected = numents - 1;
2189 break;
2190 case KEY_ENTER:
Chris Allegrettaa2c02e92001-07-02 01:31:44 +00002191 case NANO_ENTER_KEY:
Chris Allegrettaba8edea2001-01-04 05:22:46 +00002192 case 's': /* More Pico compatibility */
2193 case 'S':
Chris Allegretta0e9d3c62001-01-04 04:59:17 +00002194
2195 /* You can't cd up from / */
Rocco Corsi12f294c2001-04-14 06:50:24 +00002196 if (!strcmp(filelist[selected], "/..") && !strcmp(path, "/")) {
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002197 statusbar(_("Can't move up a directory"));
Rocco Corsi12f294c2001-04-14 06:50:24 +00002198 break;
2199 }
2200
2201 path = mallocstrcpy(path, filelist[selected]);
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002202
Chris Allegrettae1f14522001-09-19 03:19:43 +00002203#ifndef DISABLE_OPERATINGDIR
2204 /* Note: The case of the user's being completely outside the
2205 operating directory is handled elsewhere, before this
2206 point */
2207 if (operating_dir) {
2208 if (check_operating_dir(path, 0)) {
2209 statusbar(_("Can't visit parent in restricted mode"));
2210 beep();
2211 break;
2212 }
2213 }
2214#endif
2215
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002216 st = filestat(path);
2217 if (S_ISDIR(st.st_mode)) {
Rocco Corsi12f294c2001-04-14 06:50:24 +00002218 if ((test_dir = opendir(path)) == NULL) {
Chris Allegretta0e9d3c62001-01-04 04:59:17 +00002219 /* We can't open this dir for some reason. Complain */
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002220 statusbar(_("Can't open \"%s\": %s"), path, strerror(errno));
Rocco Corsi12f294c2001-04-14 06:50:24 +00002221 striponedir(path);
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002222 align(&path);
2223 break;
Chris Allegretta0e9d3c62001-01-04 04:59:17 +00002224 }
Rocco Corsi12f294c2001-04-14 06:50:24 +00002225 closedir(test_dir);
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002226
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002227 if (!strcmp("..", tail(path))) {
Chris Allegretta0e9d3c62001-01-04 04:59:17 +00002228 /* They want to go up a level, so strip off .. and the
2229 current dir */
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002230 striponedir(path);
2231 striponedir(path);
2232 align(&path);
2233 }
Chris Allegretta0e9d3c62001-01-04 04:59:17 +00002234
2235 /* Start over again with the new path value */
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002236 return do_browser(path);
2237 } else {
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002238 retval = path;
2239 abort = 1;
2240 }
2241 break;
Rocco Corsi12f294c2001-04-14 06:50:24 +00002242 /* Goto a specific directory */
2243 case 'g': /* Pico compatibility */
2244 case 'G':
2245 case NANO_GOTO_KEY:
2246
2247 curs_set(1);
2248 j = statusq(0, gotodir_list, GOTODIR_LIST_LEN, "", _("Goto Directory"));
2249 bottombars(browser_list, BROWSER_LIST_LEN);
2250 curs_set(0);
2251
Chris Allegrettae1f14522001-09-19 03:19:43 +00002252#ifndef DISABLE_OPERATINGDIR
2253 if (operating_dir) {
2254 if (check_operating_dir(answer, 0)) {
2255 statusbar(_("Can't go outside of %s in restricted mode"), operating_dir);
2256 break;
2257 }
2258 }
2259#endif
2260
Rocco Corsi12f294c2001-04-14 06:50:24 +00002261 if (j < 0) {
2262 statusbar(_("Goto Cancelled"));
2263 break;
2264 }
2265
2266 if (answer[0] != '/') {
2267 char *saveanswer = NULL;
2268
2269 saveanswer = mallocstrcpy(saveanswer, answer);
2270 answer = realloc(answer, strlen(path) + strlen(saveanswer) + 2);
2271 sprintf(answer, "%s/%s", path, saveanswer);
2272 free(saveanswer);
2273 }
2274
2275 if ((test_dir = opendir(answer)) == NULL) {
2276 /* We can't open this dir for some reason. Complain */
2277 statusbar(_("Can't open \"%s\": %s"), answer, strerror(errno));
2278 break;
2279 }
2280 closedir(test_dir);
2281
2282 /* Start over again with the new path value */
2283 path = mallocstrcpy(path, answer);
2284 return do_browser(path);
2285
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002286 /* Stuff we want to abort the browser */
2287 case 'q':
2288 case 'Q':
2289 case 'e': /* Pico compatibility, yeech */
2290 case 'E':
Chris Allegrettaa2c02e92001-07-02 01:31:44 +00002291 case NANO_CANCEL_KEY:
Chris Allegretta6e827412001-01-04 04:41:08 +00002292 case NANO_EXIT_FKEY:
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002293 abort = 1;
2294 break;
2295 }
2296 if (abort)
2297 break;
2298
Rocco Corsi12f294c2001-04-14 06:50:24 +00002299 blank_edit();
2300
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002301 if (width)
2302 i = width * editwinrows * ((selected / width) / editwinrows);
2303 else
2304 i = 0;
2305
2306 wmove(edit, 0, 0);
2307 for (j = i; j < numents && editline <= editwinrows - 1; j++) {
2308 filecols++;
2309
2310 strncpy(foo, tail(filelist[j]), strlen(tail(filelist[j])) + 1);
2311 while (strlen(foo) < longest)
2312 strcat(foo, " ");
2313 col += strlen(foo);
2314
2315 /* Put file info in the string also */
Chris Allegretta88520c92001-05-05 17:45:54 +00002316 /* We use lstat here to detect links; then, if we find a
2317 symlink, we examine it via stat() to see if it is a
Chris Allegretta0876dc92001-03-28 13:04:08 +00002318 directory or just a file symlink */
2319 lstat(filelist[j], &st);
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002320 if (S_ISDIR(st.st_mode))
2321 strcpy(foo + longest - 5, "(dir)");
2322 else {
Chris Allegretta0876dc92001-03-28 13:04:08 +00002323 if (S_ISLNK(st.st_mode)) {
2324 /* Aha! It's a symlink! Now, is it a dir? If so,
2325 mark it as such */
2326 st = filestat(filelist[j]);
2327 if (S_ISDIR(st.st_mode))
2328 strcpy(foo + longest - 5, "(dir)");
2329 else
2330 strcpy(foo + longest - 2, "--");
Chris Allegretta90d30742001-04-03 01:02:38 +00002331 } else if (st.st_size < (1 << 10)) /* less than 1 K */
2332 sprintf(foo + longest - 7, "%4d B",
Chris Allegrettad4615622001-05-23 21:54:47 +00002333 (int) st.st_size);
Chris Allegretta90d30742001-04-03 01:02:38 +00002334 else if (st.st_size >= (1 << 30)) /* at least 1 gig */
2335 sprintf(foo + longest - 7, "%4d GB",
2336 (int) st.st_size >> 30);
2337 else if (st.st_size >= (1 << 20)) /* at least 1 meg */
2338 sprintf(foo + longest - 7, "%4d MB",
2339 (int) st.st_size >> 20);
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002340 else /* Its more than 1 k and less than a meg */
Chris Allegretta90d30742001-04-03 01:02:38 +00002341 sprintf(foo + longest - 7, "%4d KB",
2342 (int) st.st_size >> 10);
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002343 }
2344
Chris Allegretta0e9d3c62001-01-04 04:59:17 +00002345 /* Hilight the currently selected file/dir */
Chris Allegretta8ce24132001-04-30 11:28:46 +00002346 if (j == selected) {
2347#ifdef ENABLE_COLOR
2348 color_on(edit, COLOR_STATUSBAR);
2349#else
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002350 wattron(edit, A_REVERSE);
Chris Allegretta8ce24132001-04-30 11:28:46 +00002351
2352#endif
2353 }
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002354 waddnstr(edit, foo, strlen(foo));
Chris Allegretta8ce24132001-04-30 11:28:46 +00002355 if (j == selected) {
2356#ifdef ENABLE_COLOR
2357 color_off(edit, COLOR_STATUSBAR);
2358#else
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002359 wattroff(edit, A_REVERSE);
Chris Allegretta8ce24132001-04-30 11:28:46 +00002360#endif
2361 }
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002362
Chris Allegretta0e9d3c62001-01-04 04:59:17 +00002363 /* And add some space between the cols */
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002364 waddstr(edit, " ");
2365 col += 2;
2366
2367 /* And if the next entry isn't going to fit on the
2368 line, move to the next one */
2369 if (col > (COLS - longest)) {
2370 editline++;
2371 wmove(edit, editline, 0);
2372 col = 0;
2373 if (width == 0)
2374 width = filecols;
2375 }
2376 }
2377 wrefresh(edit);
2378 } while ((kbinput = wgetch(edit)) != NANO_EXIT_KEY);
2379 curs_set(1);
2380 blank_edit();
2381 titlebar(NULL);
2382 edit_refresh();
Chris Allegrettac08f50d2001-01-06 18:12:43 +00002383 kb = keypad_on(edit, kb);
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002384
Chris Allegretta0e9d3c62001-01-04 04:59:17 +00002385 /* cleanup */
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002386 free_charptrarray(filelist, numents);
2387 free(foo);
2388 return retval;
2389}
Chris Allegretta150469a2001-01-05 21:13:14 +00002390
Chris Allegretta88520c92001-05-05 17:45:54 +00002391/* Browser front end, checks to see if inpath has a dir in it and, if so,
Chris Allegretta150469a2001-01-05 21:13:14 +00002392 starts do_browser from there, else from the current dir */
2393char *do_browse_from(char *inpath)
2394{
2395 struct stat st;
2396 char *tmp = NULL;
2397
2398 tmp = mallocstrcpy(tmp, inpath);
2399
Chris Allegretta90d30742001-04-03 01:02:38 +00002400
Chris Allegretta88520c92001-05-05 17:45:54 +00002401 /* If there's no / in the string, we may as well start from . */
Chris Allegretta90d30742001-04-03 01:02:38 +00002402 if (tmp == NULL || *tmp == '\0' || !strstr(tmp, "/")) {
2403#ifdef PATH_MAX
2404 char *from = getcwd(NULL, PATH_MAX+1);
2405#else
Chris Allegretta60c65422001-04-03 14:26:35 +00002406 char *from = getcwd(NULL, 0);
Chris Allegretta90d30742001-04-03 01:02:38 +00002407#endif /* PATH_MAX */
2408 return do_browser(from ? from : "./");
2409 }
Chris Allegretta150469a2001-01-05 21:13:14 +00002410
2411 /* If the string is a directory, pass do_browser that */
2412 st = filestat(tmp);
2413 if (S_ISDIR(st.st_mode))
2414 return do_browser(tmp);
2415
2416 /* Okay, there's a dir in there, but not at the end of the string...
2417 try stripping it off */
2418 striponedir(tmp);
2419 align(&tmp);
2420 return do_browser(tmp);
2421
2422}
2423
2424
2425
Chris Allegrettaf4b96012001-01-03 07:11:47 +00002426#endif
2427