File: [local] / src / usr.bin / mg / dired.c (download)
Revision 1.86, Wed Jun 26 16:48:00 2019 UTC (4 years, 11 months ago) by lum
Branch: MAIN
Changes since 1.85: +14 -1 lines
This diff uses the existing filename as the new filename if none is
specified while copying in dired mode.
|
/* $OpenBSD: dired.c,v 1.86 2019/06/26 16:48:00 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 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 */
};
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', 'g', direda, NULL
},
{
'n', 'x', diredn, NULL
},
{
CCHR('?'), CCHR('?'), direddl, NULL
},
}
};
void
dired_init(void)
{
funmap_add(dired, "dired");
funmap_add(d_create_directory, "dired-create-directory");
funmap_add(d_copy, "dired-do-copy");
funmap_add(d_expunge, "dired-do-flagged-delete");
funmap_add(d_rename, "dired-do-rename");
funmap_add(d_findfile, "dired-find-file");
funmap_add(d_ffotherwindow, "dired-find-file-other-window");
funmap_add(d_del, "dired-flag-file-deletion");
funmap_add(d_forwline, "dired-next-line");
funmap_add(d_otherwindow, "dired-other-window");
funmap_add(d_backline, "dired-previous-line");
funmap_add(d_refreshbuffer, "dired-revert");
funmap_add(d_backpage, "dired-scroll-down");
funmap_add(d_forwpage, "dired-scroll-up");
funmap_add(d_undel, "dired-unmark");
funmap_add(d_undelbak, "dired-unmark-backward");
funmap_add(d_killbuffer_cmd, "quit-window");
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) < 0) {
(void)xbasename(sname, fname, NFILEN);
dobeep();
ewprintf("Could not delete '%s'", sname);
curwp->w_dotline = tmp;
return (FALSE);
}
break;
case TRUE:
if (rmdir(fname) < 0) {
(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);
}
}
ret = (copy(frname, topath) >= 0) ? TRUE : FALSE;
if (ret != TRUE)
return (ret);
if ((bp = refreshbuffer(curbp)) == NULL)
return (FALSE);
return (showbuffer(bp, curwp, WFFULL | WFMODE));
}
/* ARGSUSED */
int
d_rename(int f, int n)
{
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);
ret = (rename(frname, topath) >= 0) ? TRUE : FALSE;
if (ret != TRUE)
return (ret);
if ((bp = refreshbuffer(curbp)) == NULL)
return (FALSE);
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);
}
/*
* 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;
}