File: [local] / src / usr.bin / mg / dired.c (download)
Revision 1.78, Mon Oct 12 19:08:39 2015 UTC (8 years, 8 months ago) by lum
Branch: MAIN
Changes since 1.77: +179 -9 lines
Maintain a list of files marked for deletion while refreshing a dired
buffer. Previously, when refreshing the buffer the files marked for
deletion would be lost.
Since the relationship between the files that have been marked for
deletion and those that exist on disk is volatile, I have chosen to
implement the discovery of marked files during the refresh function as
opposed to maintaining a dired buffer specific list.
|
/* $OpenBSD: dired.c,v 1.78 2015/10/12 19:08:39 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 <libgen.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 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 diredc[] = {
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
},
{
'c', 'g', diredc, NULL
},
{
'n', 'x', diredn, NULL
},
{
CCHR('?'), CCHR('?'), direddl, NULL
},
}
};
void
dired_init(void)
{
funmap_add(dired, "dired");
funmap_add(d_undelbak, "dired-unmark-backward");
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_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_rename, "dired-do-rename");
funmap_add(d_backpage, "dired-scroll-down");
funmap_add(d_forwpage, "dired-scroll-up");
funmap_add(d_undel, "dired-unmark");
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 (llength(curwp->w_dotp) > 0) {
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)
{
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);
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);
if (argv != NULL)
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));
}
/*
* 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");
}
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 *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(d1, &delhead, entry) {
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;
}