[BACK]Return to dired.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / mg

File: [local] / src / usr.bin / mg / dired.c (download)

Revision 1.93, Thu Jul 11 18:20:18 2019 UTC (4 years, 10 months ago) by lum
Branch: MAIN
CVS Tags: OPENBSD_6_8_BASE, OPENBSD_6_8, OPENBSD_6_7_BASE, OPENBSD_6_7, OPENBSD_6_6_BASE, OPENBSD_6_6
Changes since 1.92: +19 -19 lines

Allow functions that have 1 or more parameters receive and process
multiple arguments when evaluated in a startup file or via one of the
'eval' commands.

This diff does treat the '(' and ')' chars differently during
evaluation than previously, in-so-far as they are not ignored if they
are at the end or start of a line now. However, even though these
characters are not ignored, this diff should not change the behaviour
of an extant .mg file, with '(' and ')' chars at the end and start of
a line.  This situation is accomodated for in this diff (with limited
testing though).

/*	$OpenBSD: dired.c,v 1.93 2019/07/11 18:20:18 lum Exp $	*/

/* This file is in the public domain. */

/* dired module for mg 2a
 * by Robert A. Larson
 */

#include <sys/queue.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "def.h"
#include "funmap.h"
#include "kbd.h"

void		 dired_init(void);
static int	 dired(int, int);
static int	 d_otherwindow(int, int);
static int	 d_undel(int, int);
static int	 d_undelbak(int, int);
static int	 d_findfile(int, int);
static int	 d_ffotherwindow(int, int);
static int	 d_expunge(int, int);
static int	 d_copy(int, int);
static int	 d_del(int, int);
static int	 d_rename(int, int);
static int	 d_exec(int, struct buffer *, const char *, const char *, ...);
static int	 d_shell_command(int, int);
static int	 d_create_directory(int, int);
static int	 d_makename(struct line *, char *, size_t);
static int	 d_warpdot(struct line *, int *);
static int	 d_forwpage(int, int);
static int	 d_backpage(int, int);
static int	 d_forwline(int, int);
static int	 d_backline(int, int);
static int	 d_killbuffer_cmd(int, int);
static int	 d_refreshbuffer(int, int);
static int	 d_filevisitalt(int, int);
static int	 d_gotofile(int, int);
static void	 reaper(int);
static struct buffer	*refreshbuffer(struct buffer *);
static int	 createlist(struct buffer *);
static void	 redelete(struct buffer *);
static char 	 *findfname(struct line *, char *);

extern struct keymap_s helpmap, cXmap, metamap;

const char DDELCHAR = 'D';

/*
 * Structure which holds a linked list of file names marked for
 * deletion. Used to maintain dired buffer 'state' between refreshes.
 */
struct delentry {
	SLIST_ENTRY(delentry) entry;
	char   *fn;
};
SLIST_HEAD(slisthead, delentry) delhead = SLIST_HEAD_INITIALIZER(delhead);

static PF dirednul[] = {
	setmark,		/* ^@ */
	gotobol,		/* ^A */
	backchar,		/* ^B */
	rescan,			/* ^C */
	d_del,			/* ^D */
	gotoeol,		/* ^E */
	forwchar,		/* ^F */
	ctrlg,			/* ^G */
	NULL,			/* ^H */
};

static PF diredcl[] = {
	reposition,		/* ^L */
	d_findfile,		/* ^M */
	d_forwline,		/* ^N */
	rescan,			/* ^O */
	d_backline,		/* ^P */
	rescan,			/* ^Q */
	backisearch,		/* ^R */
	forwisearch,		/* ^S */
	rescan,			/* ^T */
	universal_argument,	/* ^U */
	d_forwpage,		/* ^V */
	rescan,			/* ^W */
	NULL			/* ^X */
};

static PF diredcz[] = {
	spawncli,		/* ^Z */
	NULL,			/* esc */
	rescan,			/* ^\ */
	rescan,			/* ^] */
	rescan,			/* ^^ */
	rescan,			/* ^_ */
	d_forwline,		/* SP */
	d_shell_command,	/* ! */
	rescan,			/* " */
	rescan,			/* # */
	rescan,			/* $ */
	rescan,			/* % */
	rescan,			/* & */
	rescan,			/* ' */
	rescan,			/* ( */
	rescan,			/* ) */
	rescan,			/* * */
	d_create_directory	/* + */
};

static PF direda[] = {
	d_filevisitalt,		/* a */
	rescan,			/* b */
	d_copy,			/* c */
	d_del,			/* d */
	d_findfile,		/* e */
	d_findfile,		/* f */
	d_refreshbuffer,	/* g */
	rescan,			/* h */
	rescan,			/* i */
	d_gotofile		/* j */
};

static PF diredn[] = {
	d_forwline,		/* n */
	d_ffotherwindow,	/* o */
	d_backline,		/* p */
	d_killbuffer_cmd,	/* q */
	d_rename,		/* r */
	rescan,			/* s */
	rescan,			/* t */
	d_undel,		/* u */
	rescan,			/* v */
	rescan,			/* w */
	d_expunge		/* x */
};

static PF direddl[] = {
	d_undelbak		/* del */
};

static PF diredbp[] = {
	d_backpage		/* v */
};

static PF dirednull[] = {
	NULL
};

static struct KEYMAPE (1) d_backpagemap = {
	1,
	1,
	rescan,
	{
		{
		'v', 'v', diredbp, NULL
		}
	}
};

static struct KEYMAPE (7) diredmap = {
	7,
	7,
	rescan,
	{
		{
			CCHR('@'), CCHR('H'), dirednul, (KEYMAP *) & helpmap
		},
		{
			CCHR('L'), CCHR('X'), diredcl, (KEYMAP *) & cXmap
		},
		{
			CCHR('['), CCHR('['), dirednull, (KEYMAP *) &
			d_backpagemap
		},
		{
			CCHR('Z'), '+', diredcz, (KEYMAP *) & metamap
		},
		{
			'a', 'j', direda, NULL
		},
		{
			'n', 'x', diredn, NULL
		},
		{
			CCHR('?'), CCHR('?'), direddl, NULL
		},
	}
};

void
dired_init(void)
{
	funmap_add(dired, "dired", 1);
	funmap_add(d_create_directory, "dired-create-directory", 1);
	funmap_add(d_copy, "dired-do-copy", 1);
	funmap_add(d_expunge, "dired-do-flagged-delete", 0);
	funmap_add(d_rename, "dired-do-rename", 1);
	funmap_add(d_findfile, "dired-find-file", 1);
	funmap_add(d_ffotherwindow, "dired-find-file-other-window", 1);
	funmap_add(d_del, "dired-flag-file-deletion", 0);
	funmap_add(d_gotofile, "dired-goto-file", 1);
	funmap_add(d_forwline, "dired-next-line", 0);
	funmap_add(d_otherwindow, "dired-other-window", 0);
	funmap_add(d_backline, "dired-previous-line", 0);
	funmap_add(d_refreshbuffer, "dired-revert", 0);
	funmap_add(d_backpage, "dired-scroll-down", 0);
	funmap_add(d_forwpage, "dired-scroll-up", 0);
	funmap_add(d_undel, "dired-unmark", 0);
	funmap_add(d_undelbak, "dired-unmark-backward", 0);
	funmap_add(d_killbuffer_cmd, "quit-window", 0);
	maps_add((KEYMAP *)&diredmap, "dired");
	dobindkey(fundamental_map, "dired", "^Xd");
}

/* ARGSUSED */
int
dired(int f, int n)
{
	char		 dname[NFILEN], *bufp, *slash;
	struct buffer	*bp;

	if (curbp->b_fname[0] != '\0') {
		(void)strlcpy(dname, curbp->b_fname, sizeof(dname));
		if ((slash = strrchr(dname, '/')) != NULL) {
			*(slash + 1) = '\0';
		}
	} else {
		if (getcwd(dname, sizeof(dname)) == NULL)
			dname[0] = '\0';
	}

	if ((bufp = eread("Dired: ", dname, NFILEN,
	    EFDEF | EFNEW | EFCR)) == NULL)
		return (ABORT);
	if (bufp[0] == '\0')
		return (FALSE);
	if ((bp = dired_(bufp)) == NULL)
		return (FALSE);

	curbp = bp;
	return (showbuffer(bp, curwp, WFFULL | WFMODE));
}

/* ARGSUSED */
int
d_otherwindow(int f, int n)
{
	char		 dname[NFILEN], *bufp, *slash;
	struct buffer	*bp;
	struct mgwin	*wp;

	if (curbp->b_fname[0] != '\0') {
		(void)strlcpy(dname, curbp->b_fname, sizeof(dname));
		if ((slash = strrchr(dname, '/')) != NULL) {
			*(slash + 1) = '\0';
		}
	} else {
		if (getcwd(dname, sizeof(dname)) == NULL)
			dname[0] = '\0';
	}

	if ((bufp = eread("Dired other window: ", dname, NFILEN,
	    EFDEF | EFNEW | EFCR)) == NULL)
		return (ABORT);
	else if (bufp[0] == '\0')
		return (FALSE);
	if ((bp = dired_(bufp)) == NULL)
		return (FALSE);
	if ((wp = popbuf(bp, WNONE)) == NULL)
		return (FALSE);
	curbp = bp;
	curwp = wp;
	return (TRUE);
}

/* ARGSUSED */
int
d_del(int f, int n)
{
	if (n < 0)
		return (FALSE);
	while (n--) {
		if (d_warpdot(curwp->w_dotp, &curwp->w_doto) == TRUE) {
			lputc(curwp->w_dotp, 0, DDELCHAR);
			curbp->b_flag |= BFDIREDDEL;
		}
		if (lforw(curwp->w_dotp) != curbp->b_headp) {
			curwp->w_dotp = lforw(curwp->w_dotp);
			curwp->w_dotline++;
		}
	}
	curwp->w_rflag |= WFEDIT | WFMOVE;
	return (d_warpdot(curwp->w_dotp, &curwp->w_doto));
}

/* ARGSUSED */
int
d_undel(int f, int n)
{
	if (n < 0)
		return (d_undelbak(f, -n));
	while (n--) {
		if (llength(curwp->w_dotp) > 0)
			lputc(curwp->w_dotp, 0, ' ');
		if (lforw(curwp->w_dotp) != curbp->b_headp) {
			curwp->w_dotp = lforw(curwp->w_dotp);
			curwp->w_dotline++;
		}
	}
	curwp->w_rflag |= WFEDIT | WFMOVE;
	return (d_warpdot(curwp->w_dotp, &curwp->w_doto));
}

/* ARGSUSED */
int
d_undelbak(int f, int n)
{
	if (n < 0)
		return (d_undel(f, -n));
	while (n--) {
		if (lback(curwp->w_dotp) != curbp->b_headp) {
			curwp->w_dotp = lback(curwp->w_dotp);
			curwp->w_dotline--;
		}
		if (llength(curwp->w_dotp) > 0)
			lputc(curwp->w_dotp, 0, ' ');
	}
	curwp->w_rflag |= WFEDIT | WFMOVE;
	return (d_warpdot(curwp->w_dotp, &curwp->w_doto));
}

/* ARGSUSED */
int
d_findfile(int f, int n)
{
	struct buffer	*bp;
	int		 s;
	char		 fname[NFILEN];

	if ((s = d_makename(curwp->w_dotp, fname, sizeof(fname))) == ABORT)
		return (FALSE);
	if (s == TRUE)
		bp = dired_(fname);
	else
		bp = findbuffer(fname);
	if (bp == NULL)
		return (FALSE);
	curbp = bp;
	if (showbuffer(bp, curwp, WFFULL) != TRUE)
		return (FALSE);
	if (bp->b_fname[0] != 0)
		return (TRUE);
	return (readin(fname));
}

/* ARGSUSED */
int
d_ffotherwindow(int f, int n)
{
	char		 fname[NFILEN];
	int		 s;
	struct buffer	*bp;
	struct mgwin	*wp;

	if ((s = d_makename(curwp->w_dotp, fname, sizeof(fname))) == ABORT)
		return (FALSE);
	if ((bp = (s ? dired_(fname) : findbuffer(fname))) == NULL)
		return (FALSE);
	if ((wp = popbuf(bp, WNONE)) == NULL)
		return (FALSE);
	curbp = bp;
	curwp = wp;
	if (bp->b_fname[0] != 0)
		return (TRUE);	/* never true for dired buffers */
	return (readin(fname));
}

/* ARGSUSED */
int
d_expunge(int f, int n)
{
	struct line	*lp, *nlp;
	char		 fname[NFILEN], sname[NFILEN];
	int		 tmp;

	tmp = curwp->w_dotline;
	curwp->w_dotline = 0;

	for (lp = bfirstlp(curbp); lp != curbp->b_headp; lp = nlp) {
		curwp->w_dotline++;
		nlp = lforw(lp);
		if (llength(lp) && lgetc(lp, 0) == 'D') {
			switch (d_makename(lp, fname, sizeof(fname))) {
			case ABORT:
				dobeep();
				ewprintf("Bad line in dired buffer");
				curwp->w_dotline = tmp;
				return (FALSE);
			case FALSE:
				if (unlink(fname) == -1) {
					(void)xbasename(sname, fname, NFILEN);
					dobeep();
					ewprintf("Could not delete '%s'", sname);
					curwp->w_dotline = tmp;
					return (FALSE);
				}
				break;
			case TRUE:
				if (rmdir(fname) == -1) {
					(void)xbasename(sname, fname, NFILEN);
					dobeep();
					ewprintf("Could not delete directory "
					    "'%s'", sname);
					curwp->w_dotline = tmp;
					return (FALSE);
				}
				break;
			}
			lfree(lp);
			curwp->w_bufp->b_lines--;
			if (tmp > curwp->w_dotline)
				tmp--;
			curwp->w_rflag |= WFFULL;
		}
	}
	curwp->w_dotline = tmp;
	d_warpdot(curwp->w_dotp, &curwp->w_doto);

	/* we have deleted all items successfully, remove del flag */
	curbp->b_flag &= ~BFDIREDDEL;

	return (TRUE);
}

/* ARGSUSED */
int
d_copy(int f, int n)
{
	struct stat      statbuf;
	char		 frname[NFILEN], toname[NFILEN], sname[NFILEN];
	char		*topath, *bufp;
	int		 ret;
	size_t		 off;
	struct buffer	*bp;

	if (d_makename(curwp->w_dotp, frname, sizeof(frname)) != FALSE) {
		dobeep();
		ewprintf("Not a file");
		return (FALSE);
	}
	off = strlcpy(toname, curbp->b_fname, sizeof(toname));
	if (off >= sizeof(toname) - 1) {	/* can't happen, really */
		dobeep();
		ewprintf("Directory name too long");
		return (FALSE);
	}
	(void)xbasename(sname, frname, NFILEN);
	bufp = eread("Copy %s to: ", toname, sizeof(toname),
	    EFDEF | EFNEW | EFCR, sname);
	if (bufp == NULL)
		return (ABORT);
	else if (bufp[0] == '\0')
		return (FALSE);

	topath = adjustname(toname, TRUE);
	if (stat(topath, &statbuf) == 0) {
		if (S_ISDIR(statbuf.st_mode)) {
			off = snprintf(toname, sizeof(toname), "%s/%s",
			    topath, sname);
			if (off < 0 || off >= sizeof(toname) - 1) {
				dobeep();
				ewprintf("Directory name too long");
				return (FALSE);
			}
			topath = adjustname(toname, TRUE);
		}
	}
	if (strcmp(frname, topath) == 0) {
		ewprintf("Cannot copy to same file: %s", frname);
		return (TRUE);
	}
	ret = (copy(frname, topath) >= 0) ? TRUE : FALSE;
	if (ret != TRUE)
		return (ret);
	if ((bp = refreshbuffer(curbp)) == NULL)
		return (FALSE);

	ewprintf("Copy: 1 file");
	return (showbuffer(bp, curwp, WFFULL | WFMODE));
}

/* ARGSUSED */
int
d_rename(int f, int n)
{
	struct stat      statbuf;
	char		 frname[NFILEN], toname[NFILEN];
	char		*topath, *bufp;
	int		 ret;
	size_t		 off;
	struct buffer	*bp;
	char		 sname[NFILEN];

	if (d_makename(curwp->w_dotp, frname, sizeof(frname)) != FALSE) {
		dobeep();
		ewprintf("Not a file");
		return (FALSE);
	}
	off = strlcpy(toname, curbp->b_fname, sizeof(toname));
	if (off >= sizeof(toname) - 1) {	/* can't happen, really */
		dobeep();
		ewprintf("Directory name too long");
		return (FALSE);
	}
	(void)xbasename(sname, frname, NFILEN);
	bufp = eread("Rename %s to: ", toname,
	    sizeof(toname), EFDEF | EFNEW | EFCR, sname);
	if (bufp == NULL)
		return (ABORT);
	else if (bufp[0] == '\0')
		return (FALSE);

	topath = adjustname(toname, TRUE);
	if (stat(topath, &statbuf) == 0) {
		if (S_ISDIR(statbuf.st_mode)) {
			off = snprintf(toname, sizeof(toname), "%s/%s",
			    topath, sname);
			if (off < 0 || off >= sizeof(toname) - 1) {
				dobeep();
				ewprintf("Directory name too long");
				return (FALSE);
			}
			topath = adjustname(toname, TRUE);
		}
	}
	if (strcmp(frname, topath) == 0) {
		ewprintf("Cannot move to same file: %s", frname);
		return (TRUE);
	}
	ret = (rename(frname, topath) >= 0) ? TRUE : FALSE;
	if (ret != TRUE)
		return (ret);
	if ((bp = refreshbuffer(curbp)) == NULL)
		return (FALSE);

	ewprintf("Move: 1 file");
	return (showbuffer(bp, curwp, WFFULL | WFMODE));
}

/* ARGSUSED */
void
reaper(int signo __attribute__((unused)))
{
	int	save_errno = errno, status;

	while (waitpid(-1, &status, WNOHANG) >= 0)
		;
	errno = save_errno;
}

/*
 * Pipe the currently selected file through a shell command.
 */
/* ARGSUSED */
int
d_shell_command(int f, int n)
{
	char		 command[512], fname[PATH_MAX], *bufp;
	struct buffer	*bp;
	struct mgwin	*wp;
	char		 sname[NFILEN];

	bp = bfind("*Shell Command Output*", TRUE);
	if (bclear(bp) != TRUE)
		return (ABORT);

	if (d_makename(curwp->w_dotp, fname, sizeof(fname)) != FALSE) {
		dobeep();
		ewprintf("bad line");
		return (ABORT);
	}

	command[0] = '\0';
	(void)xbasename(sname, fname, NFILEN);
	bufp = eread("! on %s: ", command, sizeof(command), EFNEW, sname);
	if (bufp == NULL)
		return (ABORT);

	if (d_exec(0, bp, fname, "sh", "-c", command, NULL) != TRUE)
		return (ABORT);

	if ((wp = popbuf(bp, WNONE)) == NULL)
		return (ABORT);	/* XXX - free the buffer?? */
	curwp = wp;
	curbp = wp->w_bufp;
	return (TRUE);
}

/*
 * Pipe input file to cmd and insert the command's output in the
 * given buffer.  Each line will be prefixed with the given
 * number of spaces.
 */
static int
d_exec(int space, struct buffer *bp, const char *input, const char *cmd, ...)
{
	char	 buf[BUFSIZ];
	va_list	 ap;
	struct	 sigaction olda, newa;
	char	**argv = NULL, *cp;
	FILE	*fin;
	int	 fds[2] = { -1, -1 };
	int	 infd = -1;
	int	 ret = (ABORT), n;
	pid_t	 pid;

	if (sigaction(SIGCHLD, NULL, &olda) == -1)
		return (ABORT);

	/* Find the number of arguments. */
	va_start(ap, cmd);
	for (n = 2; va_arg(ap, char *) != NULL; n++)
		;
	va_end(ap);

	/* Allocate and build the argv. */
	if ((argv = calloc(n, sizeof(*argv))) == NULL) {
		dobeep();
		ewprintf("Can't allocate argv : %s", strerror(errno));
		goto out;
	}

	n = 1;
	argv[0] = (char *)cmd;
	va_start(ap, cmd);
	while ((argv[n] = va_arg(ap, char *)) != NULL)
		n++;
	va_end(ap);

	if (input == NULL)
		input = "/dev/null";

	if ((infd = open(input, O_RDONLY)) == -1) {
		dobeep();
		ewprintf("Can't open input file : %s", strerror(errno));
		goto out;
	}

	if (pipe(fds) == -1) {
		dobeep();
		ewprintf("Can't create pipe : %s", strerror(errno));
		goto out;
	}

	newa.sa_handler = reaper;
	newa.sa_flags = 0;
	if (sigaction(SIGCHLD, &newa, NULL) == -1)
		goto out;

	if ((pid = fork()) == -1) {
		dobeep();
		ewprintf("Can't fork");
		goto out;
	}

	switch (pid) {
	case 0: /* Child */
		close(fds[0]);
		dup2(infd, STDIN_FILENO);
		dup2(fds[1], STDOUT_FILENO);
		dup2(fds[1], STDERR_FILENO);
		if (execvp(argv[0], argv) == -1)
			ewprintf("Can't exec %s: %s", argv[0], strerror(errno));
		exit(1);
		break;
	default: /* Parent */
		close(infd);
		close(fds[1]);
		infd = fds[1] = -1;
		if ((fin = fdopen(fds[0], "r")) == NULL)
			goto out;
		while (fgets(buf, sizeof(buf), fin) != NULL) {
			cp = strrchr(buf, '\n');
			if (cp == NULL && !feof(fin)) {	/* too long a line */
				int c;
				addlinef(bp, "%*s%s...", space, "", buf);
				while ((c = getc(fin)) != EOF && c != '\n')
					;
				continue;
			} else if (cp)
				*cp = '\0';
			addlinef(bp, "%*s%s", space, "", buf);
		}
		fclose(fin);
		break;
	}
	ret = (TRUE);

out:
	if (sigaction(SIGCHLD, &olda, NULL) == -1)
		ewprintf("Warning, couldn't reset previous signal handler");
	if (fds[0] != -1)
		close(fds[0]);
	if (fds[1] != -1)
		close(fds[1]);
	if (infd != -1)
		close(infd);
	free(argv);
	return ret;
}

/* ARGSUSED */
int
d_create_directory(int f, int n)
{
	int ret;
	struct buffer	*bp;

	ret = ask_makedir();
	if (ret != TRUE)
		return(ret);

	if ((bp = refreshbuffer(curbp)) == NULL)
		return (FALSE);

	return (showbuffer(bp, curwp, WFFULL | WFMODE));
}

/* ARGSUSED */
int
d_killbuffer_cmd(int f, int n)
{
	return(killbuffer_cmd(FFRAND, 0));
}

int
d_refreshbuffer(int f, int n)
{
	struct buffer *bp;

	if ((bp = refreshbuffer(curbp)) == NULL)
		return (FALSE);

	return (showbuffer(bp, curwp, WFFULL | WFMODE));
}

/*
 * Kill then re-open the requested dired buffer.
 * If required, take a note of any files marked for deletion. Then once
 * the buffer has been re-opened, remark the same files as deleted.
 */
struct buffer *
refreshbuffer(struct buffer *bp)
{
	char		*tmp_b_fname;
	int	 	 i, tmp_w_dotline, ddel = 0;

	/* remember directory path to open later */
	tmp_b_fname = strdup(bp->b_fname);
	if (tmp_b_fname == NULL) {
		dobeep();
		ewprintf("Out of memory");
		return (NULL);
	}
	tmp_w_dotline = curwp->w_dotline;

	/* create a list of files for deletion */
	if (bp->b_flag & BFDIREDDEL)
		ddel = createlist(bp);

	killbuffer(bp);

	/* dired_() uses findbuffer() to create new buffer */
	if ((bp = dired_(tmp_b_fname)) == NULL) {
		free(tmp_b_fname);
		return (NULL);
	}
	free(tmp_b_fname);

	/* remark any previously deleted files with a 'D' */
	if (ddel)
		redelete(bp);		

	/* find dot line */
	bp->b_dotp = bfirstlp(bp);
	if (tmp_w_dotline > bp->b_lines)
		tmp_w_dotline = bp->b_lines - 1;
	for (i = 1; i < tmp_w_dotline; i++)
		bp->b_dotp = lforw(bp->b_dotp);

	bp->b_dotline = i;
	bp->b_doto = 0;
	d_warpdot(bp->b_dotp, &bp->b_doto);

	curbp = bp;

	return (bp);
}

static int
d_makename(struct line *lp, char *fn, size_t len)
{
	int	 start, nlen;
	char	*namep;

	if (d_warpdot(lp, &start) == FALSE)
		return (ABORT);
	namep = &lp->l_text[start];
	nlen = llength(lp) - start;

	if (snprintf(fn, len, "%s%.*s", curbp->b_fname, nlen, namep) >= len)
		return (ABORT); /* Name is too long. */

	/* Return TRUE if the entry is a directory. */
	return ((lgetc(lp, 2) == 'd') ? TRUE : FALSE);
}

#define NAME_FIELD	9

static int
d_warpdot(struct line *dotp, int *doto)
{
	char *tp = dotp->l_text;
	int off = 0, field = 0, len;

	/*
	 * Find the byte offset to the (space-delimited) filename
	 * field in formatted ls output.
	 */
	len = llength(dotp);
	while (off < len) {
		if (tp[off++] == ' ') {
			if (++field == NAME_FIELD) {
				*doto = off;
				return (TRUE);
			}
			/* Skip the space. */
			while (off < len && tp[off] == ' ')
				off++;
		}
	}
	/* We didn't find the field. */
	*doto = 0;
	return (FALSE);
}

static int
d_forwpage(int f, int n)
{
	forwpage(f | FFRAND, n);
	return (d_warpdot(curwp->w_dotp, &curwp->w_doto));
}

static int
d_backpage (int f, int n)
{
	backpage(f | FFRAND, n);
	return (d_warpdot(curwp->w_dotp, &curwp->w_doto));
}

static int
d_forwline (int f, int n)
{
	forwline(f | FFRAND, n);
	return (d_warpdot(curwp->w_dotp, &curwp->w_doto));
}

static int
d_backline (int f, int n)
{
	backline(f | FFRAND, n);
	return (d_warpdot(curwp->w_dotp, &curwp->w_doto));
}

int
d_filevisitalt (int f, int n)
{
	char	 fname[NFILEN];

	if (d_makename(curwp->w_dotp, fname, sizeof(fname)) == ABORT)
		return (FALSE);

	return(do_filevisitalt(fname));
}

/*
 * XXX dname needs to have enough place to store an additional '/'.
 */
struct buffer *
dired_(char *dname)
{
	struct buffer	*bp;
	int		 i;
	size_t		 len;

	if ((dname = adjustname(dname, TRUE)) == NULL) {
		dobeep();
		ewprintf("Bad directory name");
		return (NULL);
	}
	/* this should not be done, instead adjustname() should get a flag */
	len = strlen(dname);
	if (dname[len - 1] != '/') {
		dname[len++] = '/';
		dname[len] = '\0';
	}
	if ((access(dname, R_OK | X_OK)) == -1) {
		if (errno == EACCES) {
			dobeep();
			ewprintf("Permission denied: %s", dname);
		}
		return (NULL);
	}
	for (bp = bheadp; bp != NULL; bp = bp->b_bufp) {
		if (strcmp(bp->b_fname, dname) == 0) {
			if (fchecktime(bp) != TRUE)
				ewprintf("Directory has changed on disk;"
				    " type g to update Dired");
			return (bp);
		}

	}
	bp = bfind(dname, TRUE);
	bp->b_flag |= BFREADONLY | BFIGNDIRTY;

	if ((d_exec(2, bp, NULL, "ls", "-al", dname, NULL)) != TRUE)
		return (NULL);

	/* Find the line with ".." on it. */
	bp->b_dotp = bfirstlp(bp);
	bp->b_dotline = 1;
	for (i = 0; i < bp->b_lines; i++) {
		bp->b_dotp = lforw(bp->b_dotp);
		bp->b_dotline++;
		if (d_warpdot(bp->b_dotp, &bp->b_doto) == FALSE)
			continue;
		if (strcmp(ltext(bp->b_dotp) + bp->b_doto, "..") == 0)
			break;
	}

	/* We want dot on the entry right after "..", if possible. */
	if (++i < bp->b_lines - 2) {
		bp->b_dotp = lforw(bp->b_dotp);
		bp->b_dotline++;
	}
	d_warpdot(bp->b_dotp, &bp->b_doto);

	(void)strlcpy(bp->b_fname, dname, sizeof(bp->b_fname));
	(void)strlcpy(bp->b_cwd, dname, sizeof(bp->b_cwd));
	if ((bp->b_modes[1] = name_mode("dired")) == NULL) {
		bp->b_modes[0] = name_mode("fundamental");
		dobeep();
		ewprintf("Could not find mode dired");
		return (NULL);
	}
	(void)fupdstat(bp);
	bp->b_nmodes = 1;
	return (bp);
}

/*
 * Iterate through the lines of the dired buffer looking for files
 * collected in the linked list made in createlist(). If a line is found
 * replace 'D' as first char in a line. As lines are found, remove the
 * corresponding item from the linked list. Iterate for as long as there
 * are items in the linked list or until end of buffer is found.
 */
void
redelete(struct buffer *bp)
{
	struct delentry	*dt, *d1 = NULL;
	struct line	*lp, *nlp;
	char		 fname[NFILEN];
	char		*p = fname;
	size_t		 plen, fnlen;
	int		 finished = 0;

	/* reset the deleted file buffer flag until a deleted file is found */
	bp->b_flag &= ~BFDIREDDEL;

	for (lp = bfirstlp(bp); lp != bp->b_headp; lp = nlp) {	
		bp->b_dotp = lp;
		if ((p = findfname(lp, p)) == NULL) {
			nlp = lforw(lp);
			continue;
		}
		plen = strlen(p);
		SLIST_FOREACH_SAFE(d1, &delhead, entry, dt) {
			fnlen = strlen(d1->fn);
			if ((plen == fnlen) && 
			    (strncmp(p, d1->fn, plen) == 0)) {
				lputc(bp->b_dotp, 0, DDELCHAR);
				bp->b_flag |= BFDIREDDEL;
				SLIST_REMOVE(&delhead, d1, delentry, entry);
				if (SLIST_EMPTY(&delhead)) {
					finished = 1;
					break;
				}	
			}
		}
		if (finished)
			break;
		nlp = lforw(lp);
	}
	while (!SLIST_EMPTY(&delhead)) {
		d1 = SLIST_FIRST(&delhead);
		SLIST_REMOVE_HEAD(&delhead, entry);
		free(d1->fn);
		free(d1);
	}
	return;
}

/*
 * Create a list of files marked for deletion.
 */
int
createlist(struct buffer *bp)
{
	struct delentry	*d1 = NULL, *d2;
	struct line	*lp, *nlp;
	char		 fname[NFILEN];
	char		*p = fname;
	int		 ret = FALSE;

	for (lp = bfirstlp(bp); lp != bp->b_headp; lp = nlp) {
		/* 
		 * Check if the line has 'D' on the first char and if a valid
		 * filename can be extracted from it.
		 */
		if (((lp->l_text[0] != DDELCHAR)) ||
		    ((p = findfname(lp, p)) == NULL)) {
			nlp = lforw(lp);
			continue;
		}
		if (SLIST_EMPTY(&delhead)) {
			if ((d1 = malloc(sizeof(struct delentry)))
			     == NULL)
				return (ABORT);
			if ((d1->fn = strdup(p)) == NULL) {
				free(d1);
				return (ABORT);
			}
			SLIST_INSERT_HEAD(&delhead, d1, entry);
		} else {
			if ((d2 = malloc(sizeof(struct delentry)))
			     == NULL) {
				free(d1->fn);
				free(d1);
				return (ABORT);
			}
			if ((d2->fn = strdup(p)) == NULL) {
				free(d1->fn);
				free(d1);
				free(d2);
				return (ABORT);
			}
			SLIST_INSERT_AFTER(d1, d2, entry);
			d1 = d2;				
		}
		ret = TRUE;
		nlp = lforw(lp);
	}
	return (ret);
}

int
d_gotofile(int f, int n)
{
	struct line	*lp, *nlp;
	struct buffer   *curbp;
	size_t		 lenfpath;
	char		 fpath[NFILEN], fname[NFILEN];
	char		*p, *fpth, *fnp = NULL;
	int		 tmp;

	if (getbufcwd(fpath, sizeof(fpath)) != TRUE)
		fpath[0] = '\0';
	lenfpath = strlen(fpath);
	fnp = eread("Goto file: ", fpath, NFILEN,
	    EFNEW | EFCR | EFFILE | EFDEF);
	if (fnp == NULL)
		return (ABORT);
	else if (fnp[0] == '\0')
		return (FALSE);

	fpth = adjustname(fpath, TRUE);		/* Removes last '/' if	*/
	if (strlen(fpth) == lenfpath - 1) {	/* directory, hence -1.	*/
		ewprintf("No file to find");	/* Current directory given so  */
		return (TRUE);			/* return at present location. */
	}
	(void)xbasename(fname, fpth, NFILEN);
	curbp = curwp->w_bufp;
	tmp = 0;
	for (lp = bfirstlp(curbp); lp != curbp->b_headp; lp = nlp) {
		tmp++;
		if ((p = findfname(lp, p)) == NULL) {
			nlp = lforw(lp);
			continue;
		}
		if (strcmp(fname, p) == 0) {
			curwp->w_dotp = lp;
			curwp->w_dotline = tmp;
			(void)d_warpdot(curwp->w_dotp, &curwp->w_doto);
			tmp--;
			break;
		}
		nlp = lforw(lp);
	}
	if (tmp == curbp->b_lines - 1) {
		ewprintf("File not found %s", fname);
		return (FALSE);
	} else {
		ewprintf("");
		return (TRUE);
	}
}

/*
 * Look for and extract a file name on a dired buffer line.
 */
char *
findfname(struct line *lp, char *fn)
{
	int start;

	(void)d_warpdot(lp, &start);
	if (start < 1)
		return NULL;
	fn = &lp->l_text[start];
	return fn;
}