files.c 33.5 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
22
23
24
25
26
27
28
29
/**************************************************************************
 *   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.            *
 *                                                                        *
 **************************************************************************/

#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
30
31
#include <ctype.h>
#include <dirent.h>
32
#include <pwd.h>
Chris Allegretta's avatar
Chris Allegretta committed
33
34
35
36

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

Chris Allegretta's avatar
Chris Allegretta committed
38
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
#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)
{
132
    long size, num_lines = 0, linetemp = 0;
Chris Allegretta's avatar
Chris Allegretta committed
133
134
135
136
137
138
139
    char input[2];		/* buffer */
    char *buf;
    long i = 0, bufx = 128;
    filestruct *fileptr = current, *tmp = NULL;
    int line1ins = 0;

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

    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);
156
	    num_lines++;
Chris Allegretta's avatar
Chris Allegretta committed
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
	    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);
179
	num_lines++;
Chris Allegretta's avatar
Chris Allegretta committed
180
181
182
	buf[0] = 0;
    }
    /* Did we even GET a file? */
183
    if (totsize == 0 || fileptr == NULL) {
Chris Allegretta's avatar
Chris Allegretta committed
184
	new_file();
185
	statusbar(_("Read %d lines"), num_lines);
Chris Allegretta's avatar
Chris Allegretta committed
186
187
	return 1;
    }
Robert Siemborski's avatar
Robert Siemborski committed
188

Chris Allegretta's avatar
Chris Allegretta committed
189
190
191
192
193
194
195
196
    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
197
	new_magicline();
Chris Allegretta's avatar
Chris Allegretta committed
198
	totsize--;
Robert Siemborski's avatar
Robert Siemborski committed
199
200

	/* Update the edit buffer */
Chris Allegretta's avatar
Chris Allegretta committed
201
202
	load_file();
    }
203
204
    statusbar(_("Read %d lines"), num_lines);
    totlines += num_lines;
Chris Allegretta's avatar
Chris Allegretta committed
205
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

    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);
231
232
	if (!insert)
	    new_file();
Chris Allegretta's avatar
Chris Allegretta committed
233
234
	return -1;
    } else {			/* File is A-OK */
235
236
237
238
239
240
241
242
	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
243
244
	    if (!insert)
		new_file();
Chris Allegretta's avatar
Chris Allegretta committed
245
246
247
248
249
250
251
252
253
254
255
256
257
	    return -1;
	}
	if (!quiet)
	    statusbar(_("Reading File"));
	read_file(fd, filename);
    }

    return 1;
}

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

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

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

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

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

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

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

	dump_buffer(fileage);
	set_modified();

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

	/* If we've gone off the bottom, recenter, otherwise just redraw */
Chris Allegretta's avatar
Chris Allegretta committed
304
	if (current->lineno > editbot->lineno)
305
	    edit_update(current, CENTER);
Chris Allegretta's avatar
Chris Allegretta committed
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
	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.
 * 
325
 * tmp means we are writing a tmp file in a secure fashion.  We use
326
 * it when spell checking or dumping the file on an error.
Chris Allegretta's avatar
Chris Allegretta committed
327
328
329
330
 */
int write_file(char *name, int tmp)
{
    long size, lineswritten = 0;
Chris Allegretta's avatar
Chris Allegretta committed
331
    static char *buf = NULL;
Chris Allegretta's avatar
Chris Allegretta committed
332
    filestruct *fileptr;
333
    int fd, mask = 0, realexists, anyexists;
334
    struct stat st, lst;
335
    static char *realname = NULL;
Chris Allegretta's avatar
Chris Allegretta committed
336
337
338
339
340

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

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

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

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

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

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

362
363
    /* New case: if the file exists, just give up */
    if (tmp && anyexists != -1)
364
	return -1;
365
    /* NOTE: If you change this statement, you MUST CHANGE the if 
366
367
368
       statement below (that says:
		if (realexists == -1 || tmp || (!ISSET(FOLLOW_SYMLINKS) &&
		S_ISLNK(lst.st_mode))) {
369
370
       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
371
	/* Use O_EXCL if tmp == 1.  This is now copied from joe, because
372
373
374
375
376
	   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));
377
378

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

Chris Allegretta's avatar
Chris Allegretta committed
389
390
391
    }
    /* Don't follow symlink.  Create new file. */
    else {
Chris Allegretta's avatar
Chris Allegretta committed
392
393
	buf = nmalloc(strlen(realname) + 8);
	strncpy(buf, realname, strlen(realname)+1);
Chris Allegretta's avatar
Chris Allegretta committed
394
395
	strcat(buf, ".XXXXXX");
	if ((fd = mkstemp(buf)) == -1) {
Chris Allegretta's avatar
Chris Allegretta committed
396
397
	    if (ISSET(TEMP_OPT)) {
		UNSET(TEMP_OPT);
398
		return do_writeout(filename, 1);
399
	    }
Chris Allegretta's avatar
Chris Allegretta committed
400
401
402
403
404
405
406
407
	    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
408
	/* Next line is so we discount the "magic line" */
409
410
	if (filebot == fileptr && fileptr->data[0] == '\0')
	    break;
Robert Siemborski's avatar
Robert Siemborski committed
411

Chris Allegretta's avatar
Chris Allegretta committed
412
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
	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) {
446
	statusbar(_("Could not close %s: %s"), realname, strerror(errno));
Chris Allegretta's avatar
Chris Allegretta committed
447
448
449
450
	unlink(buf);
	return -1;
    }

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

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

458
459
460
461
462
	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
463
	/* Use permissions from file we are overwriting. */
464
465
466
467
468
	mask = st.st_mode;

    if (!tmp && (!ISSET(FOLLOW_SYMLINKS) && S_ISLNK(lst.st_mode))) {
	if (unlink(realname) == -1) {
	    if (errno != ENOENT) {
469
		statusbar(_("Could not open %s for writing: %s"),
470
			  realname, strerror(errno));
471
472
473
		unlink(buf);
		return -1;
	    }
Chris Allegretta's avatar
Chris Allegretta committed
474
	}
475
476
477
478
479
480
481
482
483
484
485
486
487
	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
488
    }
489
490
491
492
    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
493
    if (!tmp) {
Chris Allegretta's avatar
Chris Allegretta committed
494
	filename = mallocstrcpy(filename, realname);
Chris Allegretta's avatar
Chris Allegretta committed
495
	statusbar(_("Wrote %d lines"), lineswritten);
496
	UNSET(MODIFIED);
Chris Allegretta's avatar
Chris Allegretta committed
497
	titlebar(NULL);
Chris Allegretta's avatar
Chris Allegretta committed
498
499
500
501
    }
    return 1;
}

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

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

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

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

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

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

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

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

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

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

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

#ifdef NANO_EXTRA
553
554
	    if (exiting && !ISSET(TEMP_OPT) && !strcasecmp(answer, "zzy")
		&& !did_cred) {
555
556
		do_credits();
		did_cred = 1;
557
		return -1;
558
559
	    }
#endif
560
	    if (strcmp(answer, filename)) {
Chris Allegretta's avatar
Chris Allegretta committed
561
562
563
564
565
566
		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
567
568
		}
	    }
Chris Allegretta's avatar
Chris Allegretta committed
569
570
571
572
	    i = write_file(answer, 0);

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

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

586
#ifndef DISABLE_TABCOMP
587
588
589
590

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

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

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

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

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

609
	    }
610
611
	}
	else {
612

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

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

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

624
625
626
	    free(find_user);

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

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

	    }
633

634
	    endpwent();
635
	}
636
    }
637
638

    return dirtmp;
639
640
641
}

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

    dirptr = real_dir_from_tilde(buf);

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

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

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

/*
 * 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)
{
687
    char **matches = (char **) NULL;
688
    char *matchline = NULL;
689
    struct passwd *userdata;
690

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

694
    strcat(buf, "*");
695

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

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

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

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

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

Chris Allegretta's avatar
Chris Allegretta committed
716
717
718
719
720
721
722
723
    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)
{
724
    char *dirName, *dirtmp = NULL, *tmp = NULL, *tmp2 = NULL;
Chris Allegretta's avatar
Chris Allegretta committed
725
726
727
728
    char **matches = (char **) NULL;
    DIR *dir;
    struct dirent *next;

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

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

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

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

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

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

#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
752
	if ((dirName = getcwd(NULL, 0)) == NULL)
753
#endif /* PATH_MAX */
Chris Allegretta's avatar
Chris Allegretta committed
754
755
756
757
758
759
760
761
762
763
	    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
764

765
766
767
768
769
770
771
772
773
774
775
    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
776
777
778
779
780
781
782
783
784
785
    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
786
	fprintf(stderr, "Comparing \'%s\'\n", next->d_name);
Chris Allegretta's avatar
Chris Allegretta committed
787
788
789
#endif
	/* See if this matches */
	if (check_wildcard_match(next->d_name, tmp) == TRUE) {
790
791
792
793
794
795
796
797

	    /* 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
798
	    ++*num_matches;
799
800
801
802

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

    return (matches);
}

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

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

824
825
	*lastWasTab = 1;

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

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

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

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

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

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

	/* 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);

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

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

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

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

	    if (is_dir)
		break;
887
888

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

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

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

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

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

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

916
917
918
919
920
921
922
923
924
	    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++;
		}
925
926
		if (match_matches == num_matches &&
		    (i == num_matches || matches[i] != 0)) {
927
		    /* All the matches have the same character at pos+1,
928
		       so paste it into buf... */
929
		    buf = nrealloc(buf, strlen(buf) + 2);
930
		    strncat(buf, matches[0] + pos, 1);
931
		    *newplace += 1;
932
		    pos++;
933
		} else {
934
935
936
937
		    beep();
		    break;
		}
	    }
938
	    break;
939
	}
Chris Allegretta's avatar
Chris Allegretta committed
940
941
942
943
944
945
946
947
948
949
    } 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);

950
	    editline = 0;
951

952
953
954
955
956
957
958
959
	    /* 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;

960
961
	    foo = nmalloc(longestname + 5);

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

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

		strcat(foo, "  ");

973
974
		/* Disable el cursor */
		curs_set(0);
975
976
977
978
979
980
		/* 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 */
981
		if (col > (COLS - longestname) && matches[i + 1] != NULL) {
982
983
		    editline++;
		    wmove(edit, editline, 0);
984
985
986
987
		    if (editline == editwinrows - 1) {
			waddstr(edit, _("(more)"));
			break;
		    }
Chris Allegretta's avatar
Chris Allegretta committed
988
989
990
		    col = 0;
		}
	    }
991
	    free(foo);
Chris Allegretta's avatar
Chris Allegretta committed
992
	    wrefresh(edit);
993
994
	} else
	    beep();
Chris Allegretta's avatar
Chris Allegretta committed
995
996
997
998

    }

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

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

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

1010
    stat(path, &st);
Chris Allegretta's avatar
Chris Allegretta committed
1011
1012
1013
1014
1015
1016
1017
1018
1019
    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;
1020
    int aisdir, bisdir;
Chris Allegretta's avatar
Chris Allegretta committed
1021

1022
1023
    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
1024

1025
1026
1027
1028
1029
1030
1031
1032
    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
1033
1034
1035

}

Chris Allegretta's avatar
Chris Allegretta committed
1036
1037

/* Initialize the browser code, including the list of files in *path */
Chris Allegretta's avatar
Chris Allegretta committed
1038
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
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
1092
1093
/* only print the last part of a path, isn't there a shell 
   command for this? */
Chris Allegretta's avatar
Chris Allegretta committed
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
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
1107
/* Strip one dir from the end of a string */
Chris Allegretta's avatar
Chris Allegretta committed
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
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
1131
/* Our browser function.  inpath is the path to start browsing from */
Chris Allegretta's avatar
Chris Allegretta committed
1132
1133
1134
1135
1136
1137
1138
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;
1139
    int lineno = 0, kb;
Chris Allegretta's avatar
Chris Allegretta committed
1140
1141
    char **filelist = (char **) NULL;

1142
1143
    currshortcut = browser_list;
    currslen = BROWSER_LIST_LEN;
Chris Allegretta's avatar
Chris Allegretta committed
1144
1145
1146
    /* 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
1147
1148
1149
1150
1151
    if (path != NULL && strcmp(path, inpath)) {
	free(path);
	path = NULL;
    }

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

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

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

1162
    kb = keypad_on(edit, 1);
Chris Allegretta's avatar
Chris Allegretta committed
1163
1164
1165
1166
1167
1168
1169
    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
1170
1171

    /* Loop invariant: Microsoft sucks. */
Chris Allegretta's avatar
Chris Allegretta committed
1172
1173
1174
1175
1176
    do {
	blank_edit();
	blank_statusbar();
 	editline = 0;
	col = 0;
1177
1178
1179
1180
1181
1182
	    
	/* 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
1183
1184

	switch (kbinput) {
1185
1186
1187
1188
1189
1190
1191
1192

#ifndef NANO_SMALL
#ifdef NCURSES_MOUSE_VERSION
        case KEY_MOUSE:
            do_mouse();
            break;
#endif
#endif
Chris Allegretta's avatar
Chris Allegretta committed
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
	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:
1214
	case NANO_PREVPAGE_FKEY:
Chris Allegretta's avatar
Chris Allegretta committed
1215
	case KEY_PPAGE:
1216
	case '-':
1217
1218

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

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

	    /* You can't cd up from / */
Chris Allegretta's avatar
Chris Allegretta committed
1253
1254
1255
1256
1257
1258
1259
1260
	    if (!strcmp(filelist[selected], "/..") && !strcmp(path, "/"))
		statusbar(_("Can't move up a directory"));
	    else
		path = mallocstrcpy(path, filelist[selected]);

	    st = filestat(path);
	    if (S_ISDIR(st.st_mode)) {
		if (opendir(path) == NULL) {
Chris Allegretta's avatar
Chris Allegretta committed
1261
		    /* We can't open this dir for some reason.  Complain */
Chris Allegretta's avatar
Chris Allegretta committed
1262
1263
1264
1265
		    statusbar(_("Can't open \"%s\": %s"), path, strerror(errno));
		    striponedir(path);		    
		    align(&path);
		    break;
Chris Allegretta's avatar
Chris Allegretta committed
1266
		} 
Chris Allegretta's avatar
Chris Allegretta committed
1267
1268

		if (!strcmp("..", tail(path))) {
Chris Allegretta's avatar
Chris Allegretta committed
1269
1270
		    /* They want to go up a level, so strip off .. and the
			current dir */
Chris Allegretta's avatar
Chris Allegretta committed
1271
1272
1273
1274
		    striponedir(path);
		    striponedir(path);
		    align(&path);
		}
Chris Allegretta's avatar
Chris Allegretta committed
1275
1276

		/* Start over again with the new path value */
Chris Allegretta's avatar
Chris Allegretta committed
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
		return do_browser(path);
	    } else {
		retval = path;
		abort = 1;
	    }
	    break;
	/* Stuff we want to abort the browser */
	case 'q':
	case 'Q':
	case 'e':	/* Pico compatibility, yeech */
	case 'E':
1288
	case NANO_EXIT_FKEY:
Chris Allegretta's avatar
Chris Allegretta committed
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
		abort = 1;
		break;
	}
	if (abort)
	    break;

	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 */
1310
1311
1312
1313
	    /* 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
1314
1315
1316
	    if (S_ISDIR(st.st_mode))
		strcpy(foo + longest - 5, "(dir)");
	    else {
1317
1318
1319
1320
1321
1322
1323
1324
		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, "--");
1325
1326
1327
1328
1329
1330
1331
1332
1333
		} 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
1334
		else /* Its more than 1 k and less than a meg */
1335
1336
		    sprintf(foo + longest - 7, "%4d KB", 
			(int) st.st_size >> 10);
Chris Allegretta's avatar
Chris Allegretta committed
1337
1338
	    }

Chris Allegretta's avatar
Chris Allegretta committed
1339
	    /* Hilight the currently selected file/dir */
Chris Allegretta's avatar
Chris Allegretta committed
1340
1341
1342
1343
1344
1345
	    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
1346
	    /* And add some space between the cols */
Chris Allegretta's avatar
Chris Allegretta committed
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
	    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();
1366
    kb = keypad_on(edit, kb);
Chris Allegretta's avatar
Chris Allegretta committed
1367

Chris Allegretta's avatar
Chris Allegretta committed
1368
    /* cleanup */
Chris Allegretta's avatar
Chris Allegretta committed
1369
1370
1371
1372
    free_charptrarray(filelist, numents);
    free(foo);
    return retval;
}
1373

1374
/* Browser front end, checks to see if inpath has a dir in it and if so
1375
1376
1377
1378
1379
1380
1381
1382
 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);

1383

1384
    /* If there's no / in the string, we may was well start from . */
1385
1386
1387
1388
    if (tmp == NULL || *tmp == '\0' || !strstr(tmp, "/")) {
#ifdef PATH_MAX
	char *from = getcwd(NULL, PATH_MAX+1);
#else
1389
	char *from = getcwd(NULL, 0);
1390
1391
1392
#endif /* PATH_MAX */
	return do_browser(from ? from : "./");
    }
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408

    /* 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
1409
1410
#endif