From: lum Date: Mon, 12 Oct 2015 19:08:39 +0000 (+0000) Subject: Maintain a list of files marked for deletion while refreshing a dired X-Git-Url: http://artulab.com/gitweb/?a=commitdiff_plain;h=1c4a9ba6222fa3b2381c98dc87f2a6fa2b421e54;p=openbsd 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. --- diff --git a/usr.bin/mg/def.h b/usr.bin/mg/def.h index 1ec5c97d561..22f099d89e6 100644 --- a/usr.bin/mg/def.h +++ b/usr.bin/mg/def.h @@ -1,4 +1,4 @@ -/* $OpenBSD: def.h,v 1.150 2015/09/29 02:07:49 guenther Exp $ */ +/* $OpenBSD: def.h,v 1.151 2015/10/12 19:08:39 lum Exp $ */ /* This file is in the public domain. */ @@ -288,7 +288,8 @@ struct buffer { #define BFOVERWRITE 0x08 /* overwrite mode */ #define BFREADONLY 0x10 /* read only mode */ #define BFDIRTY 0x20 /* Buffer was modified elsewhere */ -#define BFIGNDIRTY 0x40 /* Ignore modifications */ +#define BFIGNDIRTY 0x40 /* Ignore modifications */ +#define BFDIREDDEL 0x80 /* Dired has a deleted 'D' file */ /* * This structure holds information about recent actions for the Undo command. */ diff --git a/usr.bin/mg/dired.c b/usr.bin/mg/dired.c index 4d943d49f34..3fb6199c4a6 100644 --- a/usr.bin/mg/dired.c +++ b/usr.bin/mg/dired.c @@ -1,4 +1,4 @@ -/* $OpenBSD: dired.c,v 1.77 2015/09/28 11:56:17 lum Exp $ */ +/* $OpenBSD: dired.c,v 1.78 2015/10/12 19:08:39 lum Exp $ */ /* This file is in the public domain. */ @@ -53,9 +53,24 @@ 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 */ @@ -271,8 +286,10 @@ d_del(int f, int n) if (n < 0) return (FALSE); while (n--) { - if (llength(curwp->w_dotp) > 0) - lputc(curwp->w_dotp, 0, 'D'); + 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++; @@ -414,6 +431,10 @@ d_expunge(int f, int n) } 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); } @@ -692,26 +713,54 @@ d_refreshbuffer(int f, int n) 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; + char *tmp_b_fname; + int i, tmp_w_dotline, ddel = 0; - tmp = strdup(bp->b_fname); - if (tmp == NULL) { + /* 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)) == NULL) { - free(tmp); + if ((bp = dired_(tmp_b_fname)) == NULL) { + free(tmp_b_fname); return (NULL); } - free(tmp); + 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); @@ -866,3 +915,124 @@ dired_(char *dname) 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; +}