files.c 34.4 KB
Newer Older
Chris Allegretta's avatar
Chris Allegretta committed
1
/* $Id$ */
Chris Allegretta's avatar
Chris Allegretta committed
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**************************************************************************
 *   files.c                                                              *
 *                                                                        *
 *   Copyright (C) 1999 Chris Allegretta                                  *
 *   This program is free software; you can redistribute it and/or modify *
 *   it under the terms of the GNU General Public License as published by *
 *   the Free Software Foundation; either version 1, or (at your option)  *
 *   any later version.                                                   *
 *                                                                        *
 *   This program is distributed in the hope that it will be useful,      *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of       *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        *
 *   GNU General Public License for more details.                         *
 *                                                                        *
 *   You should have received a copy of the GNU General Public License    *
 *   along with this program; if not, write to the Free Software          *
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.            *
 *                                                                        *
 **************************************************************************/

22
23
#include "config.h"

Chris Allegretta's avatar
Chris Allegretta committed
24
25
26
27
28
29
30
31
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
Chris Allegretta's avatar
Chris Allegretta committed
32
33
#include <ctype.h>
#include <dirent.h>
34
#include <pwd.h>
Chris Allegretta's avatar
Chris Allegretta committed
35
36
37

#include "proto.h"
#include "nano.h"
Chris Allegretta's avatar
Chris Allegretta committed
38

Chris Allegretta's avatar
Chris Allegretta committed
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#ifndef NANO_SMALL
#include <libintl.h>
#define _(string) gettext(string)
#else
#define _(string) (string)
#endif

/* Load file into edit buffer - takes data from file struct */
void load_file(void)
{
    current = fileage;
    wmove(edit, current_y, current_x);
}

/* What happens when there is no file to open? aiee! */
void new_file(void)
{
    fileage = nmalloc(sizeof(filestruct));
    fileage->data = nmalloc(1);
    strcpy(fileage->data, "");
    fileage->prev = NULL;
    fileage->next = NULL;
    fileage->lineno = 1;
    filebot = fileage;
    edittop = fileage;
    editbot = fileage;
    current = fileage;
    totlines = 1;
    UNSET(VIEW_MODE);
}


int read_byte(int fd, char *filename, char *input)
{
    static char buf[BUFSIZ];
    static int index = 0;
    static int size = 0;

    if (index == size) {
	index = 0;
	size = read(fd, buf, BUFSIZ);
	if (size == -1) {
	    clear();
	    refresh();
	    resetty();
	    endwin();
	    perror(filename);
	}
	if (!size)
	    return 0;
    }
    *input = buf[index++];
    return 1;
}

filestruct *read_line(char *buf, filestruct * prev, int *line1ins)
{
    filestruct *fileptr;

    fileptr = nmalloc(sizeof(filestruct));
    fileptr->data = nmalloc(strlen(buf) + 2);
    strcpy(fileptr->data, buf);

    if (*line1ins) {
	/* Special case, insert with cursor on 1st line. */
	fileptr->prev = NULL;
	fileptr->next = fileage;
	fileptr->lineno = 1;
	*line1ins = 0;
	/* If we're inserting into the first line of the file, then
	   we want to make sure that our edit buffer stays on the
	   first line (and that fileage stays up to date!) */
	fileage = fileptr;
	edittop = fileptr;
    } else if (fileage == NULL) {
	fileage = fileptr;
	fileage->lineno = 1;
	fileage->next = fileage->prev = NULL;
	fileptr = filebot = fileage;
    } else if (prev) {
	fileptr->prev = prev;
	fileptr->next = NULL;
	fileptr->lineno = prev->lineno + 1;
	prev->next = fileptr;
    } else {
	die(_("read_line: not on first line and prev is NULL"));
    }

    return fileptr;
}


int read_file(int fd, char *filename)
{
133
    long size, num_lines = 0, linetemp = 0;
Chris Allegretta's avatar
Chris Allegretta committed
134
135
136
137
138
139
140
    char input[2];		/* buffer */
    char *buf;
    long i = 0, bufx = 128;
    filestruct *fileptr = current, *tmp = NULL;
    int line1ins = 0;

    buf = nmalloc(bufx);
141
    buf[0] = '\0';
Chris Allegretta's avatar
Chris Allegretta committed
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156

    if (fileptr != NULL && fileptr->prev != NULL) {
	fileptr = fileptr->prev;
	tmp = fileptr;
    } else if (fileptr != NULL && fileptr->prev == NULL) {
	tmp = fileage;
	current = fileage;
	line1ins = 1;
    }
    input[1] = 0;
    /* Read the entire file into file struct */
    while ((size = read_byte(fd, filename, input)) > 0) {
	linetemp = 0;
	if (input[0] == '\n') {
	    fileptr = read_line(buf, fileptr, &line1ins);
157
	    num_lines++;
Chris Allegretta's avatar
Chris Allegretta committed
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
	    buf[0] = 0;
	    i = 0;
	} else {
	    /* Now we allocate a bigger buffer 128 characters at a time.
	       If we allocate a lot of space for one line, we may indeed 
	       have to use a buffer this big later on, so we don't
	       decrease it at all.  We do free it at the end though. */

	    if (i >= bufx - 1) {
		buf = nrealloc(buf, bufx + 128);
		bufx += 128;
	    }
	    buf[i] = input[0];
	    buf[i + 1] = 0;
	    i++;
	}
	totsize += size;
    }

    /* Did we not get a newline but still have stuff to do? */
    if (buf[0]) {
	fileptr = read_line(buf, fileptr, &line1ins);
180
	num_lines++;
Chris Allegretta's avatar
Chris Allegretta committed
181
182
183
	buf[0] = 0;
    }
    /* Did we even GET a file? */
184
    if (totsize == 0 || fileptr == NULL) {
Chris Allegretta's avatar
Chris Allegretta committed
185
	new_file();
186
	statusbar(_("Read %d lines"), num_lines);
Chris Allegretta's avatar
Chris Allegretta committed
187
188
	return 1;
    }
Robert Siemborski's avatar
Robert Siemborski committed
189

Chris Allegretta's avatar
Chris Allegretta committed
190
191
192
193
194
195
196
197
    if (current != NULL) {
	fileptr->next = current;
	current->prev = fileptr;
	renumber(current);
	current_x = 0;
	placewewant = 0;
    } else if (fileptr->next == NULL) {
	filebot = fileptr;
Robert Siemborski's avatar
Robert Siemborski committed
198
	new_magicline();
Chris Allegretta's avatar
Chris Allegretta committed
199
	totsize--;
Robert Siemborski's avatar
Robert Siemborski committed
200
201

	/* Update the edit buffer */
Chris Allegretta's avatar
Chris Allegretta committed
202
203
	load_file();
    }
204
205
    statusbar(_("Read %d lines"), num_lines);
    totlines += num_lines;
Chris Allegretta's avatar
Chris Allegretta committed
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231

    free(buf);
    close(fd);

    return 1;
}

/* Open the file (and decide if it exists) */
int open_file(char *filename, int insert, int quiet)
{
    int fd;
    struct stat fileinfo;

    if (!strcmp(filename, "") || stat(filename, &fileinfo) == -1) {
	if (insert) {
	    if (!quiet)
		statusbar(_("\"%s\" not found"), filename);
	    return -1;
	} else {
	    /* We have a new file */
	    statusbar(_("New File"));
	    new_file();
	}
    } else if ((fd = open(filename, O_RDONLY)) == -1) {
	if (!quiet)
	    statusbar("%s: %s", strerror(errno), filename);
232
233
	if (!insert)
	    new_file();
Chris Allegretta's avatar
Chris Allegretta committed
234
235
	return -1;
    } else {			/* File is A-OK */
236
237
238
239
240
241
242
243
	if (S_ISDIR(fileinfo.st_mode) || S_ISCHR(fileinfo.st_mode) || 
		S_ISBLK(fileinfo.st_mode)) {
	    if (S_ISDIR(fileinfo.st_mode))
		statusbar(_("File \"%s\" is a directory"), filename);
	    else
		/* Don't open character or block files.  Sorry, /dev/sndstat! */
		statusbar(_("File \"%s\" is a device file"), filename);

Chris Allegretta's avatar
Chris Allegretta committed
244
245
	    if (!insert)
		new_file();
Chris Allegretta's avatar
Chris Allegretta committed
246
247
248
249
250
251
252
253
254
255
256
257
258
	    return -1;
	}
	if (!quiet)
	    statusbar(_("Reading File"));
	read_file(fd, filename);
    }

    return 1;
}

int do_insertfile(void)
{
    int i;
259
    char *realname = NULL;
Chris Allegretta's avatar
Chris Allegretta committed
260
261

    wrap_reset();
262
263
    currshortcut = writefile_list;
    currslen = WRITEFILE_LIST_LEN;
264
    i = statusq(1, writefile_list, WRITEFILE_LIST_LEN, "",
Chris Allegretta's avatar
Chris Allegretta committed
265
266
267
268
269
270
271
		_("File to insert [from ./] "));
    if (i != -1) {

#ifdef DEBUG
	fprintf(stderr, "filename is %s", answer);
#endif

272
#ifndef DISABLE_TABCOMP
273
	realname = real_dir_from_tilde(answer);
274
#else
275
	realname = mallocstrcpy(realname, answer);
276
#endif
277

278
#ifndef DISABLE_BROWSER
Chris Allegretta's avatar
Chris Allegretta committed
279
	if (i == NANO_TOFILES_KEY) {
280
281
	    
	    char *tmp = do_browse_from(realname);
282
283
	    currshortcut = writefile_list;
	    currslen = WRITEFILE_LIST_LEN;
Chris Allegretta's avatar
Chris Allegretta committed
284

285
#ifdef DISABLE_TABCOMP
286
	    realname = NULL;
287
288
#endif
	    if 	(tmp != NULL)
289
		realname = mallocstrcpy(realname, tmp);
290
	    else
291
		return do_insertfile();
Chris Allegretta's avatar
Chris Allegretta committed
292
293
294
	}
#endif

295
296
	i = open_file(realname, 1, 0);
	free(realname);
Chris Allegretta's avatar
Chris Allegretta committed
297
298
299
300
301

	dump_buffer(fileage);
	set_modified();

	/* Here we want to rebuild the edit window */
Robert Siemborski's avatar
Robert Siemborski committed
302
	fix_editbot();
Chris Allegretta's avatar
Chris Allegretta committed
303
304

	/* If we've gone off the bottom, recenter, otherwise just redraw */
Chris Allegretta's avatar
Chris Allegretta committed
305
	if (current->lineno > editbot->lineno)
306
	    edit_update(current, CENTER);
Chris Allegretta's avatar
Chris Allegretta committed
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
	else
	    edit_refresh();

	UNSET(KEEP_CUTBUFFER);
	display_main_list();
	return i;
    } else {
	statusbar(_("Cancelled"));
	UNSET(KEEP_CUTBUFFER);
	display_main_list();
	return 0;
    }
}

/*
 * Write a file out.  If tmp is nonzero, we set the umask to 0600,
 * we don't set the global variable filename to it's name, and don't
 * print out how many lines we wrote on the statusbar.
 * 
326
 * tmp means we are writing a tmp file in a secure fashion.  We use
327
 * it when spell checking or dumping the file on an error.
Chris Allegretta's avatar
Chris Allegretta committed
328
329
330
331
 */
int write_file(char *name, int tmp)
{
    long size, lineswritten = 0;
Chris Allegretta's avatar
Chris Allegretta committed
332
    static char *buf = NULL;
Chris Allegretta's avatar
Chris Allegretta committed
333
    filestruct *fileptr;
334
    int fd, mask = 0, realexists, anyexists;
335
    struct stat st, lst;
336
    static char *realname = NULL;
Chris Allegretta's avatar
Chris Allegretta committed
337
338
339
340
341

    if (!strcmp(name, "")) {
	statusbar(_("Cancelled"));
	return -1;
    }
Chris Allegretta's avatar
Chris Allegretta committed
342
    titlebar(NULL);
Chris Allegretta's avatar
Chris Allegretta committed
343
    fileptr = fileage;
344
345
346
347

    if (realname != NULL)
	free(realname);

Chris Allegretta's avatar
Chris Allegretta committed
348
349
350
    if (buf != NULL)
	free(buf);

351
#ifndef DISABLE_TABCOMP
352
353
354
355
    realname = real_dir_from_tilde(name);
#else
    realname = mallocstrcpy(realname, name);
#endif
Chris Allegretta's avatar
Chris Allegretta committed
356

357
    /* Save the state of file at the end of the symlink (if there is one) */
358
359
    realexists = stat(realname, &st);

360
361
    /* Stat the link itself for the check... */
    anyexists = lstat(realname, &lst);
362

363
364
    /* New case: if the file exists, just give up */
    if (tmp && anyexists != -1)
365
	return -1;
366
    /* NOTE: If you change this statement, you MUST CHANGE the if 
367
368
369
       statement below (that says:
		if (realexists == -1 || tmp || (!ISSET(FOLLOW_SYMLINKS) &&
		S_ISLNK(lst.st_mode))) {
370
371
       to reflect whether or not to link/unlink/rename the file */
    else if (ISSET(FOLLOW_SYMLINKS) || !S_ISLNK(lst.st_mode) || tmp) {
Chris Allegretta's avatar
Chris Allegretta committed
372
	/* Use O_EXCL if tmp == 1.  This is now copied from joe, because
373
374
375
376
377
	   wiggy says so *shrug*. */
	if (tmp)
	    fd = open(realname, O_WRONLY | O_CREAT | O_EXCL, (S_IRUSR|S_IWUSR));
	else
	    fd = open(realname, O_WRONLY | O_CREAT | O_TRUNC, (S_IRUSR|S_IWUSR));
378
379

	/* First, just give up if we couldn't even open the file */
380
	if (fd == -1) {
381
	    if (!tmp && ISSET(TEMP_OPT)) {
Chris Allegretta's avatar
Chris Allegretta committed
382
		UNSET(TEMP_OPT);
383
		return do_writeout(filename, 1);
Chris Allegretta's avatar
Chris Allegretta committed
384
	    }
Chris Allegretta's avatar
Chris Allegretta committed
385
386
387
388
	    statusbar(_("Could not open file for writing: %s"),
		      strerror(errno));
	    return -1;
	}
389

Chris Allegretta's avatar
Chris Allegretta committed
390
391
392
    }
    /* Don't follow symlink.  Create new file. */
    else {
Chris Allegretta's avatar
Chris Allegretta committed
393
394
	buf = nmalloc(strlen(realname) + 8);
	strncpy(buf, realname, strlen(realname)+1);
Chris Allegretta's avatar
Chris Allegretta committed
395
396
	strcat(buf, ".XXXXXX");
	if ((fd = mkstemp(buf)) == -1) {
Chris Allegretta's avatar
Chris Allegretta committed
397
398
	    if (ISSET(TEMP_OPT)) {
		UNSET(TEMP_OPT);
399
		return do_writeout(filename, 1);
400
	    }
Chris Allegretta's avatar
Chris Allegretta committed
401
402
403
404
405
406
407
408
	    statusbar(_("Could not open file for writing: %s"),
		      strerror(errno));
	    return -1;
	}
    }

    dump_buffer(fileage);
    while (fileptr != NULL && fileptr->next != NULL) {
Robert Siemborski's avatar
Robert Siemborski committed
409
	/* Next line is so we discount the "magic line" */
410
411
	if (filebot == fileptr && fileptr->data[0] == '\0')
	    break;
Robert Siemborski's avatar
Robert Siemborski committed
412

Chris Allegretta's avatar
Chris Allegretta committed
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
	size = write(fd, fileptr->data, strlen(fileptr->data));
	if (size == -1) {
	    statusbar(_("Could not open file for writing: %s"),
		      strerror(errno));
	    return -1;
	} else {
#ifdef DEBUG
	    fprintf(stderr, _("Wrote >%s\n"), fileptr->data);
#endif
	}
	write(fd, "\n", 1);

	fileptr = fileptr->next;
	lineswritten++;
    }

    if (fileptr != NULL) {
	size = write(fd, fileptr->data, strlen(fileptr->data));
	if (size == -1) {
	    statusbar(_("Could not open file for writing: %s"),
		      strerror(errno));
	    return -1;
	} else if (size > 0) {
	    size = write(fd, "\n", 1);
	    if (size == -1) {
		statusbar(_("Could not open file for writing: %s"),
			  strerror(errno));
		return -1;
	    }
	}
    }


    if (close(fd) == -1) {
447
	statusbar(_("Could not close %s: %s"), realname, strerror(errno));
Chris Allegretta's avatar
Chris Allegretta committed
448
449
450
451
	unlink(buf);
	return -1;
    }

452
453
    if (realexists == -1 || tmp ||
	(!ISSET(FOLLOW_SYMLINKS) && S_ISLNK(lst.st_mode))) {
Chris Allegretta's avatar
Chris Allegretta committed
454

455
456
457
	/* Use default umask as file permisions if file is a new file. */
	mask = umask(0);
	umask(mask);
Chris Allegretta's avatar
Chris Allegretta committed
458

459
460
461
462
463
	if (tmp)		/* We don't want anyone reading our temporary file! */
	    mask = 0600;
	else
	    mask = 0666 & ~mask;
    } else
Chris Allegretta's avatar
Chris Allegretta committed
464
	/* Use permissions from file we are overwriting. */
465
466
467
468
469
	mask = st.st_mode;

    if (!tmp && (!ISSET(FOLLOW_SYMLINKS) && S_ISLNK(lst.st_mode))) {
	if (unlink(realname) == -1) {
	    if (errno != ENOENT) {
470
		statusbar(_("Could not open %s for writing: %s"),
471
			  realname, strerror(errno));
472
473
474
		unlink(buf);
		return -1;
	    }
Chris Allegretta's avatar
Chris Allegretta committed
475
	}
476
477
478
479
480
481
482
483
484
485
486
487
488
	if (link(buf, realname) != -1)
	    unlink(buf);
	else if (errno != EPERM) {
	    statusbar(_("Could not open %s for writing: %s"),
		      name, strerror(errno));
	    unlink(buf);
	    return -1;
	} else if (rename(buf, realname) == -1) {	/* Try a rename?? */
	    statusbar(_("Could not open %s for writing: %s"),
		      realname, strerror(errno));
	    unlink(buf);
	    return -1;
	}
Chris Allegretta's avatar
Chris Allegretta committed
489
    }
490
491
492
493
    if (chmod(realname, mask) == -1)
	statusbar(_("Could not set permissions %o on %s: %s"),
		  mask, realname, strerror(errno));

Chris Allegretta's avatar
Chris Allegretta committed
494
    if (!tmp) {
Chris Allegretta's avatar
Chris Allegretta committed
495
	filename = mallocstrcpy(filename, realname);
Chris Allegretta's avatar
Chris Allegretta committed
496
	statusbar(_("Wrote %d lines"), lineswritten);
497
	UNSET(MODIFIED);
Chris Allegretta's avatar
Chris Allegretta committed
498
	titlebar(NULL);
Chris Allegretta's avatar
Chris Allegretta committed
499
500
501
502
    }
    return 1;
}

503
int do_writeout(char *path, int exiting)
Chris Allegretta's avatar
Chris Allegretta committed
504
505
{
    int i = 0;
Chris Allegretta's avatar
Chris Allegretta committed
506

507
508
509
#ifdef NANO_EXTRA
    static int did_cred = 0;
#endif
Chris Allegretta's avatar
Chris Allegretta committed
510

511
512
    currshortcut = writefile_list;
    currslen = WRITEFILE_LIST_LEN;
513
    answer = mallocstrcpy(answer, path);
Chris Allegretta's avatar
Chris Allegretta committed
514

515
    if ((exiting) && (ISSET(TEMP_OPT))) {
516
	if (filename[0]) {
517
518
519
	    i = write_file(answer, 0);
	    display_main_list();
	    return i;
520
	} else {
Chris Allegretta's avatar
Chris Allegretta committed
521
522
523
524
525
	    UNSET(TEMP_OPT);
	    do_exit();

	    /* They cancelled, abort quit */
	    return -1;
526
	}
Chris Allegretta's avatar
Chris Allegretta committed
527
528
529
    }

    while (1) {
530
	i = statusq(1, writefile_list, WRITEFILE_LIST_LEN, answer,
Chris Allegretta's avatar
Chris Allegretta committed
531
		    _("File Name to write"));
Chris Allegretta's avatar
Chris Allegretta committed
532

Chris Allegretta's avatar
Chris Allegretta committed
533
	if (i != -1) {
Chris Allegretta's avatar
Chris Allegretta committed
534

535
#ifndef DISABLE_BROWSER
Chris Allegretta's avatar
Chris Allegretta committed
536
	if (i == NANO_TOFILES_KEY) {
537
538

	    char *tmp = do_browse_from(answer);
539
540
	    currshortcut = writefile_list;
	    currslen = WRITEFILE_LIST_LEN;
Chris Allegretta's avatar
Chris Allegretta committed
541

542
543
	    if (tmp != NULL)
		answer = mallocstrcpy(answer, tmp);
544
	    else
545
		return do_writeout(answer, exiting);
Chris Allegretta's avatar
Chris Allegretta committed
546
547
548
	}
#endif

Chris Allegretta's avatar
Chris Allegretta committed
549
550
551
#ifdef DEBUG
	    fprintf(stderr, _("filename is %s"), answer);
#endif
552
553

#ifdef NANO_EXTRA
554
555
	    if (exiting && !ISSET(TEMP_OPT) && !strcasecmp(answer, "zzy")
		&& !did_cred) {
556
557
		do_credits();
		did_cred = 1;
558
		return -1;
559
560
	    }
#endif
561
	    if (strcmp(answer, filename)) {
Chris Allegretta's avatar
Chris Allegretta committed
562
563
564
565
566
567
		struct stat st;
		if (!stat(answer, &st)) {
		    i = do_yesno(0, 0, _("File exists, OVERWRITE ?"));

		    if (!i || (i == -1))
			continue;
Chris Allegretta's avatar
Chris Allegretta committed
568
569
		}
	    }
Chris Allegretta's avatar
Chris Allegretta committed
570
571
572
573
	    i = write_file(answer, 0);

	    display_main_list();
	    return i;
Chris Allegretta's avatar
Chris Allegretta committed
574
	} else {
Chris Allegretta's avatar
Chris Allegretta committed
575
576
577
	    statusbar(_("Cancelled"));
	    display_main_list();
	    return 0;
Chris Allegretta's avatar
Chris Allegretta committed
578
	}
Chris Allegretta's avatar
Chris Allegretta committed
579
580
581
582
583
    }
}

int do_writeout_void(void)
{
584
    return do_writeout(filename, 0);
Chris Allegretta's avatar
Chris Allegretta committed
585
}
Chris Allegretta's avatar
Chris Allegretta committed
586

587
#ifndef DISABLE_TABCOMP
588
589
590
591

/* Return a malloc()ed string containing the actual directory, used
 * to convert ~user and ~/ notation...
 */
592
char *real_dir_from_tilde(char *buf)
593
{
594
595
    char *dirtmp = NULL, *find_user = NULL;
    int i = 1;
596
    struct passwd *userdata;
597

598
599
600
    /* set a default value for dirtmp, in the case user home dir not found */
    dirtmp = mallocstrcpy(dirtmp, buf);

601
    if (buf[0] == '~') {
602
	if (buf[1] == 0 || buf[1] == '/') {
603
	    if (getenv("HOME") != NULL) {
604
605

		free(dirtmp);
606
607
		dirtmp = nmalloc(strlen(buf) + 2 + strlen(getenv("HOME")));

608
		sprintf(dirtmp, "%s%s", getenv("HOME"), &buf[1]);
609

610
	    }
611
612
	}
	else {
613

614
	    /* Figure how how much of of the str we need to compare */
615
616
617
618
619
	    for (i = 1; buf[i] != '/' && buf[i] != 0; i++)
		;

	    find_user = mallocstrcpy(find_user, &buf[1]);
	    find_user[i - 1] = 0;
620

621
	    for (userdata = getpwent(); userdata != NULL && 
622
		  strcmp(userdata->pw_name, find_user); 
623
		  userdata = getpwent());
624

625
626
627
	    free(find_user);

	    if (userdata != NULL) {  /* User found */
628

629
630
631
632
633
	        free(dirtmp);
		dirtmp = nmalloc(strlen(buf) + 2 + strlen(userdata->pw_dir));
		sprintf(dirtmp, "%s%s", userdata->pw_dir, &buf[i]);

	    }
634

635
	    endpwent();
636
	}
637
    }
638
639

    return dirtmp;
640
641
642
}

/* Tack a slash onto the string we're completing if it's a directory */
643
int append_slash_if_dir(char *buf, int *lastWasTab, int *place)
644
645
646
{
    char *dirptr;
    struct stat fileinfo;
647
    int ret = 0;
648
649
650
651

    dirptr = real_dir_from_tilde(buf);

    if (stat(dirptr, &fileinfo) == -1)
652
	ret = 0;
653
654
655
656
657
    else if (S_ISDIR(fileinfo.st_mode)) {
	strncat(buf, "/", 1);
	*place += 1;
	/* now we start over again with # of tabs so far */
	*lastWasTab = 0;
658
	ret = 1;
659
660
661
    }

    if (dirptr != buf)
662
	free(dirptr);
663
664

    return ret;
665
}
Chris Allegretta's avatar
Chris Allegretta committed
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687

/*
 * These functions (username_tab_completion, cwd_tab_completion, and
 * input_tab were taken from busybox 0.46 (cmdedit.c).  Here is the notice
 * from that file:
 *
 * Termios command line History and Editting, originally
 * intended for NetBSD sh (ash)
 * Copyright (c) 1999
 *      Main code:            Adam Rogoyski <rogoyski@cs.utexas.edu>
 *      Etc:                  Dave Cinege <dcinege@psychosis.com>
 *  Majorly adjusted/re-written for busybox:
 *                            Erik Andersen <andersee@debian.org>
 *
 * You may use this code as you wish, so long as the original author(s)
 * are attributed in any redistributions of the source code.
 * This code is 'as is' with no warranty.
 * This code may safely be consumed by a BSD or GPL license.
 */

char **username_tab_completion(char *buf, int *num_matches)
{
688
    char **matches = (char **) NULL;
689
    char *matchline = NULL;
690
    struct passwd *userdata;
691

692
    *num_matches = 0;
693
    matches = nmalloc(BUFSIZ * sizeof(char *));
694

695
    strcat(buf, "*");
696

697
    while ((userdata = getpwent()) != NULL) {
698

699
	if (check_wildcard_match(userdata->pw_name, &buf[1]) == TRUE) {
700
701
702
703

	    /* Cool, found a match.  Add it to the list
	     * This makes a lot more sense to me (Chris) this way...
	     */
704

705
706
707
708
	    matchline = nmalloc(strlen(userdata->pw_name) + 2);
	    sprintf(matchline, "~%s", userdata->pw_name);
	    matches[*num_matches] = matchline;
	    ++*num_matches;
709

710
711
712
	    /* If there's no more room, bail out */
	    if (*num_matches == BUFSIZ)
		break;
713
	}
714
715
    }
    endpwent();
716

Chris Allegretta's avatar
Chris Allegretta committed
717
718
719
720
721
722
723
724
    return (matches);
}

/* This was originally called exe_n_cwd_tab_completion, but we're not
   worried about executables, only filenames :> */

char **cwd_tab_completion(char *buf, int *num_matches)
{
725
    char *dirName, *dirtmp = NULL, *tmp = NULL, *tmp2 = NULL;
Chris Allegretta's avatar
Chris Allegretta committed
726
727
728
729
    char **matches = (char **) NULL;
    DIR *dir;
    struct dirent *next;

730
    matches = nmalloc(BUFSIZ * sizeof(char *));
Chris Allegretta's avatar
Chris Allegretta committed
731
732
733
734

    /* Stick a wildcard onto the buf, for later use */
    strcat(buf, "*");

735
    /* Okie, if there's a / in the buffer, strip out the directory part */
Chris Allegretta's avatar
Chris Allegretta committed
736
    if (strcmp(buf, "") && strstr(buf, "/")) {
737
	dirName = nmalloc(strlen(buf) + 1);
Chris Allegretta's avatar
Chris Allegretta committed
738
739
	tmp = buf + strlen(buf);
	while (*tmp != '/' && tmp != buf)
740
	    tmp--;
741

Chris Allegretta's avatar
Chris Allegretta committed
742
743
	tmp++;

744
745
746
	strncpy(dirName, buf, tmp - buf + 1);
	dirName[tmp - buf] = 0;

Chris Allegretta's avatar
Chris Allegretta committed
747
    } else {
748
749
750
751
752

#ifdef PATH_MAX
	if ((dirName = getcwd(NULL, PATH_MAX+1)) == NULL)
#else
	/* The better, but aparently segfault-causing way */
Chris Allegretta's avatar
Chris Allegretta committed
753
	if ((dirName = getcwd(NULL, 0)) == NULL)
754
#endif /* PATH_MAX */
Chris Allegretta's avatar
Chris Allegretta committed
755
756
757
758
759
760
761
762
763
764
	    return matches;
	else
	    tmp = buf;
    }

#ifdef DEBUG
    fprintf(stderr, "\nDir = %s\n", dirName);
    fprintf(stderr, "\nbuf = %s\n", buf);
    fprintf(stderr, "\ntmp = %s\n", tmp);
#endif
765

766
767
768
769
770
771
772
773
774
775
776
    dirtmp = real_dir_from_tilde(dirName);
    free(dirName);
    dirName = dirtmp;

#ifdef DEBUG
    fprintf(stderr, "\nDir = %s\n", dirName);
    fprintf(stderr, "\nbuf = %s\n", buf);
    fprintf(stderr, "\ntmp = %s\n", tmp);
#endif


Chris Allegretta's avatar
Chris Allegretta committed
777
778
779
780
781
782
783
784
785
786
    dir = opendir(dirName);
    if (!dir) {
	/* Don't print an error, just shut up and return */
	*num_matches = 0;
	beep();
	return (matches);
    }
    while ((next = readdir(dir)) != NULL) {

#ifdef DEBUG
787
	fprintf(stderr, "Comparing \'%s\'\n", next->d_name);
Chris Allegretta's avatar
Chris Allegretta committed
788
789
790
#endif
	/* See if this matches */
	if (check_wildcard_match(next->d_name, tmp) == TRUE) {
791
792
793
794
795
796
797
798

	    /* Cool, found a match.  Add it to the list
	     * This makes a lot more sense to me (Chris) this way...
	     */
	    tmp2 = NULL;
	    tmp2 = nmalloc(strlen(next->d_name) + 1);
	    strcpy(tmp2, next->d_name);
	    matches[*num_matches] = tmp2;
Chris Allegretta's avatar
Chris Allegretta committed
799
	    ++*num_matches;
800
801
802
803

	    /* If there's no more room, bail out */
	    if (*num_matches == BUFSIZ)
		break;
Chris Allegretta's avatar
Chris Allegretta committed
804
805
806
807
808
809
	}
    }

    return (matches);
}

810
/* This function now has an arg which refers to how much the 
Chris Allegretta's avatar
Chris Allegretta committed
811
812
 * statusbar (place) should be advanced, i.e. the new cursor pos.
 */
813
char *input_tab(char *buf, int place, int *lastWasTab, int *newplace)
Chris Allegretta's avatar
Chris Allegretta committed
814
815
{
    /* Do TAB completion */
816
    static int num_matches = 0, match_matches = 0;
Chris Allegretta's avatar
Chris Allegretta committed
817
    static char **matches = (char **) NULL;
818
    int pos = place, i = 0, col = 0, editline = 0;
819
    int longestname = 0, is_dir = 0;
820
    char *foo;
Chris Allegretta's avatar
Chris Allegretta committed
821

822
    if (*lastWasTab == FALSE) {
823
	char *tmp, *copyto, *matchBuf;
Chris Allegretta's avatar
Chris Allegretta committed
824

825
826
	*lastWasTab = 1;

Chris Allegretta's avatar
Chris Allegretta committed
827
828
	/* Make a local copy of the string -- up to the position of the
	   cursor */
829
	matchBuf = (char *) nmalloc((strlen(buf) + 2) * sizeof(char));
830
	memset(matchBuf, '\0', (strlen(buf) + 2));
831

Chris Allegretta's avatar
Chris Allegretta committed
832
833
834
835
	strncpy(matchBuf, buf, place);
	tmp = matchBuf;

	/* skip any leading white space */
836
	while (*tmp && isspace((int) *tmp))
Chris Allegretta's avatar
Chris Allegretta committed
837
838
839
840
	    ++tmp;

	/* Free up any memory already allocated */
	if (matches != NULL) {
841
842
	    for (i = i; i < num_matches; i++)
		free(matches[i]);
Chris Allegretta's avatar
Chris Allegretta committed
843
844
	    free(matches);
	    matches = (char **) NULL;
845
	    num_matches = 0;
Chris Allegretta's avatar
Chris Allegretta committed
846
847
848
849
850
	}

	/* If the word starts with `~' and there is no slash in the word, 
	 * then try completing this word as a username. */

851
	/* FIXME -- this check is broken! */
852
853
	if (*tmp == '~' && !strchr(tmp, '/'))
	    matches = username_tab_completion(tmp, &num_matches);
Chris Allegretta's avatar
Chris Allegretta committed
854
855
856
857
858
859
860
861
862

	/* Try to match everything in the current working directory that
	 * matches.  */
	if (!matches)
	    matches = cwd_tab_completion(tmp, &num_matches);

	/* Don't leak memory */
	free(matchBuf);

863
864
865
#ifdef DEBUG
	fprintf(stderr, "%d matches found...\n", num_matches);
#endif
Chris Allegretta's avatar
Chris Allegretta committed
866
	/* Did we find exactly one match? */
867
	switch (num_matches) {
868
	case 0:
869
	    blank_edit();
870
	    wrefresh(edit);
871
872
	    break;
	case 1:
873
874
875
876

	    buf = nrealloc(buf, strlen(buf) + strlen(matches[0]) + 1);

	    if (strcmp(buf, "") && strstr(buf, "/")) {
877
878
		for (tmp = buf + strlen(buf); *tmp != '/' && tmp != buf;
		     tmp--);
879
		tmp++;
880
	    } else
881
882
		tmp = buf;

883
884
885
886
887
	    if (!strcmp(tmp, matches[0]))
		is_dir = append_slash_if_dir(buf, lastWasTab, newplace);

	    if (is_dir)
		break;
888
889

	    copyto = tmp;
890
891
	    for (pos = 0; *tmp == matches[0][pos] &&
		 pos <= strlen(matches[0]); pos++)
892
893
		tmp++;

894
	    /* write out the matched name */
895
	    strncpy(copyto, matches[0], strlen(matches[0]) + 1);
896
897
	    *newplace += strlen(matches[0]) - pos;

898
899
900
	    /* Is it a directory? */
	    append_slash_if_dir(buf, lastWasTab, newplace);

901
902
	    break;
	default:
903
	    /* Check to see if all matches share a beginning, and if so
904
	       tack it onto buf and then beep */
905

906
	    if (strcmp(buf, "") && strstr(buf, "/")) {
907
908
		for (tmp = buf + strlen(buf); *tmp != '/' && tmp != buf;
		     tmp--);
909
		tmp++;
910
	    } else
911
912
913
		tmp = buf;

	    for (pos = 0; *tmp == matches[0][pos] && *tmp != 0 &&
914
		 pos <= strlen(matches[0]); pos++)
915
916
		tmp++;

917
918
919
920
921
922
923
924
925
	    while (1) {
		match_matches = 0;

		for (i = 0; i < num_matches; i++) {
		    if (matches[i][pos] == 0)
			break;
		    else if (matches[i][pos] == matches[0][pos])
			match_matches++;
		}
926
927
		if (match_matches == num_matches &&
		    (i == num_matches || matches[i] != 0)) {
928
		    /* All the matches have the same character at pos+1,
929
		       so paste it into buf... */
930
		    buf = nrealloc(buf, strlen(buf) + 2);
931
		    strncat(buf, matches[0] + pos, 1);
932
		    *newplace += 1;
933
		    pos++;
934
		} else {
935
936
937
938
		    beep();
		    break;
		}
	    }
939
	    break;
940
	}
Chris Allegretta's avatar
Chris Allegretta committed
941
942
943
944
945
946
947
948
949
950
    } else {
	/* Ok -- the last char was a TAB.  Since they
	 * just hit TAB again, print a list of all the
	 * available choices... */
	if (matches && num_matches > 0) {

	    /* Blank the edit window, and print the matches out there */
	    blank_edit();
	    wmove(edit, 0, 0);

951
	    editline = 0;
952

953
954
955
956
957
958
959
960
	    /* Figure out the length of the longest filename */
	    for (i = 0; i < num_matches; i++)
		if (strlen(matches[i]) > longestname)
		    longestname = strlen(matches[i]);

	    if (longestname > COLS - 1)
		longestname = COLS - 1;

961
962
	    foo = nmalloc(longestname + 5);

Chris Allegretta's avatar
Chris Allegretta committed
963
964
	    /* Print the list of matches */
	    for (i = 0, col = 0; i < num_matches; i++) {
965

966
		/* make each filename shown be the same length as the longest
967
		   filename, with two spaces at the end */
968
969
970
971
972
973
		snprintf(foo, longestname + 1, matches[i]);
		while (strlen(foo) < longestname)
		    strcat(foo, " ");

		strcat(foo, "  ");

974
975
		/* Disable el cursor */
		curs_set(0);
976
977
978
979
980
981
		/* now, put the match on the screen */
		waddnstr(edit, foo, strlen(foo));
		col += strlen(foo);

		/* And if the next match isn't going to fit on the
		   line, move to the next one */
982
		if (col > (COLS - longestname) && matches[i + 1] != NULL) {
983
984
		    editline++;
		    wmove(edit, editline, 0);
985
986
987
988
		    if (editline == editwinrows - 1) {
			waddstr(edit, _("(more)"));
			break;
		    }
Chris Allegretta's avatar
Chris Allegretta committed
989
990
991
		    col = 0;
		}
	    }
992
	    free(foo);
Chris Allegretta's avatar
Chris Allegretta committed
993
	    wrefresh(edit);
994
995
	} else
	    beep();
Chris Allegretta's avatar
Chris Allegretta committed
996
997
998
999

    }

    edit_refresh();
1000
1001
    curs_set(1);
    return buf;
Chris Allegretta's avatar
Chris Allegretta committed
1002
}
1003
#endif
Chris Allegretta's avatar
Chris Allegretta committed
1004

1005
#ifndef DISABLE_BROWSER
Chris Allegretta's avatar
Chris Allegretta committed
1006
1007
1008
1009
1010

/* Return the stat of the file pointed to by path */
struct stat filestat(const char *path) {
    struct stat st;

1011
    stat(path, &st);
Chris Allegretta's avatar
Chris Allegretta committed
1012
1013
1014
1015
1016
1017
1018
1019
1020
    return st;
}

/* Our sort routine for file listings - sort directories before
 * files, and then alphabetically
 */ 
int diralphasort(const void *va, const void *vb) {
    struct stat file1info, file2info;
    char *a = *(char **)va, *b = *(char **)vb;
1021
    int aisdir, bisdir;
Chris Allegretta's avatar
Chris Allegretta committed
1022

1023
1024
    aisdir = (stat(a, &file1info) != -1) && S_ISDIR(file1info.st_mode);
    bisdir = (stat(b, &file2info) != -1) && S_ISDIR(file2info.st_mode);
Chris Allegretta's avatar
Chris Allegretta committed
1025

1026
1027
1028
1029
1030
1031
1032
1033
    if (aisdir && !bisdir) return -1;
    if (!aisdir && bisdir) return 1;

#ifdef HAVE_STRCASECMP
    return(strcasecmp(a,b));
#else
    return(strcmp(a,b));
#endif
Chris Allegretta's avatar
Chris Allegretta committed
1034
1035
1036

}

Chris Allegretta's avatar
Chris Allegretta committed
1037
1038

/* Initialize the browser code, including the list of files in *path */
Chris Allegretta's avatar
Chris Allegretta committed
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
char **browser_init(char *path, int *longest, int *numents)
{
    DIR *dir;
    struct dirent *next;
    char **filelist = (char **) NULL;
    int i = 0;

    dir = opendir(path);
    if (!dir) 
	return NULL;

    *numents = 0;
    while ((next = readdir(dir)) != NULL) {
	if (!strcmp(next->d_name, "."))
	   continue;
	(*numents)++;
	if (strlen(next->d_name) > *longest)
	    *longest = strlen(next->d_name);
    }
    rewinddir(dir);
    *longest += 10;

    filelist = nmalloc(*numents * sizeof (char *));

    while ((next = readdir(dir)) != NULL) {
	if (!strcmp(next->d_name, "."))
	   continue;
	filelist[i] = nmalloc(strlen(next->d_name) + strlen(path) + 2);

	if (!strcmp(path, "/"))
	    snprintf(filelist[i], strlen(next->d_name) + strlen(path) + 1, 
			"%s%s", path, next->d_name);
	else
	    snprintf(filelist[i], strlen(next->d_name) + strlen(path) + 2, 
			"%s/%s", path, next->d_name);
	i++;
    }

    if (*longest > COLS - 1)
	*longest = COLS - 1;

    return filelist;
}

/* Free our malloced memory */
void free_charptrarray(char **array, int len)
{
    int i;

    for (i = 0; i < len - 1; i++)
	free(array[i]);
    free(array);
}

Chris Allegretta's avatar
Chris Allegretta committed
1093
1094
/* only print the last part of a path, isn't there a shell 
   command for this? */
Chris Allegretta's avatar
Chris Allegretta committed
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
char *tail(char *foo)
{
    char *tmp = NULL;

    tmp = foo + strlen(foo);
    while (*tmp != '/' && tmp != foo)
	tmp--;

    tmp++;

    return tmp;
}

Chris Allegretta's avatar
Chris Allegretta committed
1108
/* Strip one dir from the end of a string */
Chris Allegretta's avatar
Chris Allegretta committed
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
void striponedir(char *foo)
{
    char *tmp = NULL;

    /* Don't strip the root dir */
    if (!strcmp(foo, "/"))
	return;

    tmp = foo + strlen(foo);
    if (*tmp == '/')
	tmp--;

    while (*tmp != '/' && tmp != foo)
	tmp--;

    if (tmp != foo)
	*tmp = 0;
    else
	*(tmp+1) = 0;

    return;
}

Chris Allegretta's avatar
Chris Allegretta committed
1132
/* Our browser function.  inpath is the path to start browsing from */
Chris Allegretta's avatar
Chris Allegretta committed
1133
1134
1135
1136
1137
1138
1139
char *do_browser(char *inpath)
{
    struct stat st;
    char *foo, *retval = NULL;
    static char *path = NULL;
    int numents = 0, i = 0, j = 0, kbinput = 0, longest = 0, abort = 0;
    int col = 0, selected = 0, editline = 0, width = 0, filecols = 0;
1140
    int lineno = 0, kb;
Chris Allegretta's avatar
Chris Allegretta committed
1141
1142
    char **filelist = (char **) NULL;

1143
1144
    currshortcut = browser_list;
    currslen = BROWSER_LIST_LEN;
Chris Allegretta's avatar
Chris Allegretta committed
1145
1146
1147
    /* If path isn't the same as inpath, we are being passed a new
	dir as an arg.  We free it here so it will be copied from 
	inpath below */
Chris Allegretta's avatar
Chris Allegretta committed
1148
1149
1150
1151
1152
    if (path != NULL && strcmp(path, inpath)) {
	free(path);
	path = NULL;
    }

Chris Allegretta's avatar
Chris Allegretta committed
1153
    /* if path doesn't exist, make it so */
Chris Allegretta's avatar
Chris Allegretta committed
1154
1155
1156
1157
1158
    if (path == NULL)
	path = mallocstrcpy(path, inpath);

    filelist = browser_init(path, &longest, &numents);
    foo = nmalloc(longest + 8);
Chris Allegretta's avatar
Chris Allegretta committed
1159
1160

    /* Sort the list by directory first then alphabetically */
Chris Allegretta's avatar
Chris Allegretta committed
1161
1162
    qsort(filelist, numents, sizeof(char *), diralphasort);

1163
    kb = keypad_on(edit, 1);
Chris Allegretta's avatar
Chris Allegretta committed
1164
1165
1166
1167
1168
1169
1170
    titlebar(path);
    bottombars(browser_list, BROWSER_LIST_LEN);
    curs_set(0);
    wmove(edit, 0, 0);
    i = 0;
    width = 0;
    filecols = 0;
Chris Allegretta's avatar
Chris Allegretta committed
1171
1172

    /* Loop invariant: Microsoft sucks. */
Chris Allegretta's avatar
Chris Allegretta committed
1173
    do {
Rocco Corsi's avatar
   
Rocco Corsi committed
1174
1175
1176
1177
	DIR *test_dir;

	blank_statusbar_refresh();

Chris Allegretta's avatar
Chris Allegretta committed
1178
1179
 	editline = 0;
	col = 0;
1180
1181
1182
1183
1184
1185
	    
	/* Compute line number we're on now so we don't divide by zero later */
	if (width == 0)
	    lineno = selected;
	else
	    lineno = selected / width;
Chris Allegretta's avatar
Chris Allegretta committed
1186
1187

	switch (kbinput) {
1188
1189
1190
1191
1192
1193
1194
1195

#ifndef NANO_SMALL
#ifdef NCURSES_MOUSE_VERSION
        case KEY_MOUSE:
            do_mouse();
            break;
#endif
#endif
Chris Allegretta's avatar
Chris Allegretta committed
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
	case KEY_UP:
	case 'u':
	    if (selected - width >= 0)
		selected -= width;
	    break;
	case KEY_LEFT:
	case 'l':
	    if (selected > 0)
		selected--;
	    break;
	case KEY_DOWN:
	case 'd':
	    if (selected + width <= numents - 1)
		selected += width;
	    break;
	case KEY_RIGHT:
	case 'r':
	    if (selected < numents - 1)
		selected++;
	    break;
	case NANO_PREVPAGE_KEY:
1217
	case NANO_PREVPAGE_FKEY:
Chris Allegretta's avatar
Chris Allegretta committed
1218
	case KEY_PPAGE:
1219
	case '-':
1220
1221

	    if (lineno % editwinrows == 0) {
1222
1223
		if (selected - (editwinrows * width) >= 0)
		    selected -= editwinrows * width; 
Chris Allegretta's avatar
Chris Allegretta committed
1224
1225
1226
1227
		else
		    selected = 0;
	    }
	    else if (selected - (editwinrows + 
1228
1229
1230
		lineno % editwinrows) * width  >= 0)

		selected -= (editwinrows + lineno % editwinrows) * width; 
Chris Allegretta's avatar
Chris Allegretta committed
1231
1232
1233
1234
	    else
		selected = 0;
	    break;
	case NANO_NEXTPAGE_KEY:
1235
	case NANO_NEXTPAGE_FKEY:
Chris Allegretta's avatar
Chris Allegretta committed
1236
	case KEY_NPAGE:	
1237
	case ' ':
1238
	    if (lineno % editwinrows == 0) {
1239
1240
		if (selected + (editwinrows * width) <= numents - 1)
		    selected += editwinrows * width; 
Chris Allegretta's avatar
Chris Allegretta committed
1241
1242
1243
1244
		else
		    selected = numents - 1;
	    }
	    else if (selected + (editwinrows - 
1245
1246
			lineno %  editwinrows) * width <= numents - 1)
 		selected += (editwinrows - lineno % editwinrows) * width; 
Chris Allegretta's avatar
Chris Allegretta committed
1247
1248
1249
1250
1251
 	    else
		selected = numents - 1;
	    break;
	case KEY_ENTER:
	case NANO_CONTROL_M:
1252
1253
	case 's': /* More Pico compatibility */
	case 'S':
Chris Allegretta's avatar
Chris Allegretta committed
1254
1255

	    /* You can't cd up from / */
Rocco Corsi's avatar
   
Rocco Corsi committed
1256
	    if (!strcmp(filelist[selected], "/..") && !strcmp(path, "/")) {
Chris Allegretta's avatar
Chris Allegretta committed
1257
		statusbar(_("Can't move up a directory"));
Rocco Corsi's avatar
   
Rocco Corsi committed
1258
1259
1260
1261
		break;
	    }

	    path = mallocstrcpy(path, filelist[selected]);
Chris Allegretta's avatar
Chris Allegretta committed
1262
1263
1264

	    st = filestat(path);
	    if (S_ISDIR(st.st_mode)) {
Rocco Corsi's avatar
   
Rocco Corsi committed
1265
		if ((test_dir = opendir(path)) == NULL) {
Chris Allegretta's avatar
Chris Allegretta committed
1266
		    /* We can't open this dir for some reason.  Complain */
Chris Allegretta's avatar
Chris Allegretta committed
1267
		    statusbar(_("Can't open \"%s\": %s"), path, strerror(errno));
Rocco Corsi's avatar
   
Rocco Corsi committed
1268
		    striponedir(path);
Chris Allegretta's avatar
Chris Allegretta committed
1269
1270
		    align(&path);
		    break;
Chris Allegretta's avatar
Chris Allegretta committed
1271
		} 
Rocco Corsi's avatar
   
Rocco Corsi committed
1272
		closedir(test_dir);
Chris Allegretta's avatar
Chris Allegretta committed
1273
1274

		if (!strcmp("..", tail(path))) {
Chris Allegretta's avatar
Chris Allegretta committed
1275
1276
		    /* They want to go up a level, so strip off .. and the
			current dir */
Chris Allegretta's avatar
Chris Allegretta committed
1277
1278
1279
1280
		    striponedir(path);
		    striponedir(path);
		    align(&path);
		}
Chris Allegretta's avatar
Chris Allegretta committed
1281
1282

		/* Start over again with the new path value */
Chris Allegretta's avatar
Chris Allegretta committed
1283
1284
1285
1286
1287
1288
		return do_browser(path);
	    } else {
		retval = path;
		abort = 1;
	    }
	    break;
Rocco Corsi's avatar
   
Rocco Corsi committed
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
	/* Goto a specific directory */
	case 'g':	/* Pico compatibility */
	case 'G':
	case NANO_GOTO_KEY:

	    curs_set(1);
	    j = statusq(0, gotodir_list, GOTODIR_LIST_LEN, "", _("Goto Directory"));
	    bottombars(browser_list, BROWSER_LIST_LEN);
	    curs_set(0);

	    if (j < 0) {
		statusbar(_("Goto Cancelled"));
		break;
	    }

	    if (answer[0] != '/') {
		char *saveanswer = NULL;

		saveanswer = mallocstrcpy(saveanswer, answer);
		answer = realloc(answer, strlen(path) + strlen(saveanswer) + 2);
		sprintf(answer, "%s/%s", path, saveanswer);
		free(saveanswer);
	    }

	    if ((test_dir = opendir(answer)) == NULL) {
		/* We can't open this dir for some reason.  Complain */
		statusbar(_("Can't open \"%s\": %s"), answer, strerror(errno));
		break;
	    } 
	    closedir(test_dir);

	    /* Start over again with the new path value */
	    path = mallocstrcpy(path, answer);
	    return do_browser(path);

Chris Allegretta's avatar
Chris Allegretta committed
1324
1325
1326
1327
1328
	/* Stuff we want to abort the browser */
	case 'q':
	case 'Q':
	case 'e':	/* Pico compatibility, yeech */
	case 'E':
1329
	case NANO_EXIT_FKEY:
Chris Allegretta's avatar
Chris Allegretta committed
1330
1331
1332
1333
1334
1335
		abort = 1;
		break;
	}
	if (abort)
	    break;

Rocco Corsi's avatar
   
Rocco Corsi committed
1336
1337
	blank_edit();

Chris Allegretta's avatar
Chris Allegretta committed
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
	if (width)
	    i = width * editwinrows * ((selected / width) / editwinrows);
	else
	    i = 0;

	wmove(edit, 0, 0);
	for (j = i; j < numents && editline <= editwinrows - 1; j++) {
	    filecols++;

	    strncpy(foo, tail(filelist[j]), strlen(tail(filelist[j])) + 1);
	    while (strlen(foo) < longest)
		strcat(foo, " ");
	    col += strlen(foo);

	    /* Put file info in the string also */
1353
1354
1355
1356
	    /* We use lstat here to detect links, then if we find a
		symlink we examine it via stat() to see if it is a
		directory or just a file symlink */
	    lstat(filelist[j], &st);
Chris Allegretta's avatar
Chris Allegretta committed
1357
1358
1359
	    if (S_ISDIR(st.st_mode))
		strcpy(foo + longest - 5, "(dir)");
	    else {
1360
1361
1362
1363
1364
1365
1366
1367
		if (S_ISLNK(st.st_mode)) {
		     /* Aha!  It's a symlink!  Now, is it a dir?  If so,
			mark it as such */
		    st = filestat(filelist[j]);
		    if (S_ISDIR(st.st_mode))
			strcpy(foo + longest - 5, "(dir)");
		    else
			strcpy(foo + longest - 2, "--");
1368
1369
1370
1371
1372
1373
1374
1375
1376
		} else if (st.st_size < (1 << 10)) /* less than 1 K */
		    sprintf(foo + longest - 7, "%4d  B", 
			(int) st.st_size >> 10);
		else if (st.st_size >= (1 << 30)) /* at least 1 gig */
		    sprintf(foo + longest - 7, "%4d GB", 
			(int) st.st_size >> 30);
		else if (st.st_size >= (1 << 20)) /* at least 1 meg */
		    sprintf(foo + longest - 7, "%4d MB", 
			(int) st.st_size >>     20);
Chris Allegretta's avatar
Chris Allegretta committed
1377
		else /* Its more than 1 k and less than a meg */
1378
1379
		    sprintf(foo + longest - 7, "%4d KB", 
			(int) st.st_size >> 10);
Chris Allegretta's avatar
Chris Allegretta committed
1380
1381
	    }

Chris Allegretta's avatar
Chris Allegretta committed
1382
	    /* Hilight the currently selected file/dir */
Chris Allegretta's avatar
Chris Allegretta committed
1383
1384
1385
1386
1387
1388
	    if (j == selected)
		wattron(edit, A_REVERSE);
	    waddnstr(edit, foo, strlen(foo));
	    if (j == selected)
		wattroff(edit, A_REVERSE);

Chris Allegretta's avatar
Chris Allegretta committed
1389
	    /* And add some space between the cols */
Chris Allegretta's avatar
Chris Allegretta committed
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
	    waddstr(edit, "  ");
	    col += 2;

	    /* And if the next entry isn't going to fit on the
		line, move to the next one */
	    if (col > (COLS - longest)) {
		editline++;
		wmove(edit, editline, 0);
		col = 0;
		if (width == 0)
		    width = filecols;
	    }
	}
 	wrefresh(edit);
    } while ((kbinput = wgetch(edit)) != NANO_EXIT_KEY);
    curs_set(1);
    blank_edit();
    titlebar(NULL); 
    edit_refresh();
1409
    kb = keypad_on(edit, kb);
Chris Allegretta's avatar
Chris Allegretta committed
1410

Chris Allegretta's avatar
Chris Allegretta committed
1411
    /* cleanup */
Chris Allegretta's avatar
Chris Allegretta committed
1412
1413
1414
1415
    free_charptrarray(filelist, numents);
    free(foo);
    return retval;
}
1416

1417
/* Browser front end, checks to see if inpath has a dir in it and if so
1418
1419
1420
1421
1422
1423
1424
1425
 starts do_browser from there, else from the current dir */
char *do_browse_from(char *inpath)
{
    struct stat st;
    char *tmp = NULL;

    tmp = mallocstrcpy(tmp, inpath);

1426

1427
    /* If there's no / in the string, we may was well start from . */
1428
1429
1430
1431
    if (tmp == NULL || *tmp == '\0' || !strstr(tmp, "/")) {
#ifdef PATH_MAX
	char *from = getcwd(NULL, PATH_MAX+1);
#else
1432
	char *from = getcwd(NULL, 0);
1433
1434
1435
#endif /* PATH_MAX */
	return do_browser(from ? from : "./");
    }
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451

    /* If the string is a directory, pass do_browser that */
    st = filestat(tmp);
    if (S_ISDIR(st.st_mode))
	return do_browser(tmp);

    /* Okay, there's a dir in there, but not at the end of the string... 
       try stripping it off */
    striponedir(tmp);
    align(&tmp);
    return do_browser(tmp);

}



Chris Allegretta's avatar
Chris Allegretta committed
1452
1453
#endif