xref: /openbsd-src/usr.bin/mg/dired.c (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
1 /*	$OpenBSD: dired.c,v 1.82 2016/01/02 10:39:19 lum Exp $	*/
2 
3 /* This file is in the public domain. */
4 
5 /* dired module for mg 2a
6  * by Robert A. Larson
7  */
8 
9 #include <sys/queue.h>
10 #include <sys/resource.h>
11 #include <sys/stat.h>
12 #include <sys/time.h>
13 #include <sys/types.h>
14 #include <sys/wait.h>
15 #include <ctype.h>
16 #include <err.h>
17 #include <errno.h>
18 #include <fcntl.h>
19 #include <libgen.h>
20 #include <limits.h>
21 #include <signal.h>
22 #include <stdarg.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <unistd.h>
27 
28 #include "def.h"
29 #include "funmap.h"
30 #include "kbd.h"
31 
32 void		 dired_init(void);
33 static int	 dired(int, int);
34 static int	 d_otherwindow(int, int);
35 static int	 d_undel(int, int);
36 static int	 d_undelbak(int, int);
37 static int	 d_findfile(int, int);
38 static int	 d_ffotherwindow(int, int);
39 static int	 d_expunge(int, int);
40 static int	 d_copy(int, int);
41 static int	 d_del(int, int);
42 static int	 d_rename(int, int);
43 static int	 d_exec(int, struct buffer *, const char *, const char *, ...);
44 static int	 d_shell_command(int, int);
45 static int	 d_create_directory(int, int);
46 static int	 d_makename(struct line *, char *, size_t);
47 static int	 d_warpdot(struct line *, int *);
48 static int	 d_forwpage(int, int);
49 static int	 d_backpage(int, int);
50 static int	 d_forwline(int, int);
51 static int	 d_backline(int, int);
52 static int	 d_killbuffer_cmd(int, int);
53 static int	 d_refreshbuffer(int, int);
54 static int	 d_filevisitalt(int, int);
55 static void	 reaper(int);
56 static struct buffer	*refreshbuffer(struct buffer *);
57 static int	 createlist(struct buffer *);
58 static void	 redelete(struct buffer *);
59 static char 	 *findfname(struct line *, char *);
60 
61 extern struct keymap_s helpmap, cXmap, metamap;
62 
63 const char DDELCHAR = 'D';
64 
65 /*
66  * Structure which holds a linked list of file names marked for
67  * deletion. Used to maintain dired buffer 'state' between refreshes.
68  */
69 struct delentry {
70 	SLIST_ENTRY(delentry) entry;
71 	char   *fn;
72 };
73 SLIST_HEAD(slisthead, delentry) delhead = SLIST_HEAD_INITIALIZER(delhead);
74 
75 static PF dirednul[] = {
76 	setmark,		/* ^@ */
77 	gotobol,		/* ^A */
78 	backchar,		/* ^B */
79 	rescan,			/* ^C */
80 	d_del,			/* ^D */
81 	gotoeol,		/* ^E */
82 	forwchar,		/* ^F */
83 	ctrlg,			/* ^G */
84 	NULL,			/* ^H */
85 };
86 
87 static PF diredcl[] = {
88 	reposition,		/* ^L */
89 	d_findfile,		/* ^M */
90 	d_forwline,		/* ^N */
91 	rescan,			/* ^O */
92 	d_backline,		/* ^P */
93 	rescan,			/* ^Q */
94 	backisearch,		/* ^R */
95 	forwisearch,		/* ^S */
96 	rescan,			/* ^T */
97 	universal_argument,	/* ^U */
98 	d_forwpage,		/* ^V */
99 	rescan,			/* ^W */
100 	NULL			/* ^X */
101 };
102 
103 static PF diredcz[] = {
104 	spawncli,		/* ^Z */
105 	NULL,			/* esc */
106 	rescan,			/* ^\ */
107 	rescan,			/* ^] */
108 	rescan,			/* ^^ */
109 	rescan,			/* ^_ */
110 	d_forwline,		/* SP */
111 	d_shell_command,	/* ! */
112 	rescan,			/* " */
113 	rescan,			/* # */
114 	rescan,			/* $ */
115 	rescan,			/* % */
116 	rescan,			/* & */
117 	rescan,			/* ' */
118 	rescan,			/* ( */
119 	rescan,			/* ) */
120 	rescan,			/* * */
121 	d_create_directory	/* + */
122 };
123 
124 static PF direda[] = {
125 	d_filevisitalt,		/* a */
126 	rescan,			/* b */
127 	d_copy,			/* c */
128 	d_del,			/* d */
129 	d_findfile,		/* e */
130 	d_findfile,		/* f */
131 	d_refreshbuffer		/* g */
132 };
133 
134 static PF diredn[] = {
135 	d_forwline,		/* n */
136 	d_ffotherwindow,	/* o */
137 	d_backline,		/* p */
138 	d_killbuffer_cmd,	/* q */
139 	d_rename,		/* r */
140 	rescan,			/* s */
141 	rescan,			/* t */
142 	d_undel,		/* u */
143 	rescan,			/* v */
144 	rescan,			/* w */
145 	d_expunge		/* x */
146 };
147 
148 static PF direddl[] = {
149 	d_undelbak		/* del */
150 };
151 
152 static PF diredbp[] = {
153 	d_backpage		/* v */
154 };
155 
156 static PF dirednull[] = {
157 	NULL
158 };
159 
160 static struct KEYMAPE (1) d_backpagemap = {
161 	1,
162 	1,
163 	rescan,
164 	{
165 		{
166 		'v', 'v', diredbp, NULL
167 		}
168 	}
169 };
170 
171 static struct KEYMAPE (7) diredmap = {
172 	7,
173 	7,
174 	rescan,
175 	{
176 		{
177 			CCHR('@'), CCHR('H'), dirednul, (KEYMAP *) & helpmap
178 		},
179 		{
180 			CCHR('L'), CCHR('X'), diredcl, (KEYMAP *) & cXmap
181 		},
182 		{
183 			CCHR('['), CCHR('['), dirednull, (KEYMAP *) &
184 			d_backpagemap
185 		},
186 		{
187 			CCHR('Z'), '+', diredcz, (KEYMAP *) & metamap
188 		},
189 		{
190 			'a', 'g', direda, NULL
191 		},
192 		{
193 			'n', 'x', diredn, NULL
194 		},
195 		{
196 			CCHR('?'), CCHR('?'), direddl, NULL
197 		},
198 	}
199 };
200 
201 void
202 dired_init(void)
203 {
204 	funmap_add(dired, "dired");
205 	funmap_add(d_undelbak, "dired-unmark-backward");
206 	funmap_add(d_create_directory, "dired-create-directory");
207 	funmap_add(d_copy, "dired-do-copy");
208 	funmap_add(d_expunge, "dired-do-flagged-delete");
209 	funmap_add(d_findfile, "dired-find-file");
210 	funmap_add(d_ffotherwindow, "dired-find-file-other-window");
211 	funmap_add(d_del, "dired-flag-file-deletion");
212 	funmap_add(d_forwline, "dired-next-line");
213 	funmap_add(d_otherwindow, "dired-other-window");
214 	funmap_add(d_backline, "dired-previous-line");
215 	funmap_add(d_rename, "dired-do-rename");
216 	funmap_add(d_backpage, "dired-scroll-down");
217 	funmap_add(d_forwpage, "dired-scroll-up");
218 	funmap_add(d_undel, "dired-unmark");
219 	funmap_add(d_killbuffer_cmd, "quit-window");
220 	maps_add((KEYMAP *)&diredmap, "dired");
221 	dobindkey(fundamental_map, "dired", "^Xd");
222 }
223 
224 /* ARGSUSED */
225 int
226 dired(int f, int n)
227 {
228 	char		 dname[NFILEN], *bufp, *slash;
229 	struct buffer	*bp;
230 
231 	if (curbp->b_fname[0] != '\0') {
232 		(void)strlcpy(dname, curbp->b_fname, sizeof(dname));
233 		if ((slash = strrchr(dname, '/')) != NULL) {
234 			*(slash + 1) = '\0';
235 		}
236 	} else {
237 		if (getcwd(dname, sizeof(dname)) == NULL)
238 			dname[0] = '\0';
239 	}
240 
241 	if ((bufp = eread("Dired: ", dname, NFILEN,
242 	    EFDEF | EFNEW | EFCR)) == NULL)
243 		return (ABORT);
244 	if (bufp[0] == '\0')
245 		return (FALSE);
246 	if ((bp = dired_(bufp)) == NULL)
247 		return (FALSE);
248 
249 	curbp = bp;
250 	return (showbuffer(bp, curwp, WFFULL | WFMODE));
251 }
252 
253 /* ARGSUSED */
254 int
255 d_otherwindow(int f, int n)
256 {
257 	char		 dname[NFILEN], *bufp, *slash;
258 	struct buffer	*bp;
259 	struct mgwin	*wp;
260 
261 	if (curbp->b_fname[0] != '\0') {
262 		(void)strlcpy(dname, curbp->b_fname, sizeof(dname));
263 		if ((slash = strrchr(dname, '/')) != NULL) {
264 			*(slash + 1) = '\0';
265 		}
266 	} else {
267 		if (getcwd(dname, sizeof(dname)) == NULL)
268 			dname[0] = '\0';
269 	}
270 
271 	if ((bufp = eread("Dired other window: ", dname, NFILEN,
272 	    EFDEF | EFNEW | EFCR)) == NULL)
273 		return (ABORT);
274 	else if (bufp[0] == '\0')
275 		return (FALSE);
276 	if ((bp = dired_(bufp)) == NULL)
277 		return (FALSE);
278 	if ((wp = popbuf(bp, WNONE)) == NULL)
279 		return (FALSE);
280 	curbp = bp;
281 	curwp = wp;
282 	return (TRUE);
283 }
284 
285 /* ARGSUSED */
286 int
287 d_del(int f, int n)
288 {
289 	if (n < 0)
290 		return (FALSE);
291 	while (n--) {
292 		if (d_warpdot(curwp->w_dotp, &curwp->w_doto) == TRUE) {
293 			lputc(curwp->w_dotp, 0, DDELCHAR);
294 			curbp->b_flag |= BFDIREDDEL;
295 		}
296 		if (lforw(curwp->w_dotp) != curbp->b_headp) {
297 			curwp->w_dotp = lforw(curwp->w_dotp);
298 			curwp->w_dotline++;
299 		}
300 	}
301 	curwp->w_rflag |= WFEDIT | WFMOVE;
302 	return (d_warpdot(curwp->w_dotp, &curwp->w_doto));
303 }
304 
305 /* ARGSUSED */
306 int
307 d_undel(int f, int n)
308 {
309 	if (n < 0)
310 		return (d_undelbak(f, -n));
311 	while (n--) {
312 		if (llength(curwp->w_dotp) > 0)
313 			lputc(curwp->w_dotp, 0, ' ');
314 		if (lforw(curwp->w_dotp) != curbp->b_headp) {
315 			curwp->w_dotp = lforw(curwp->w_dotp);
316 			curwp->w_dotline++;
317 		}
318 	}
319 	curwp->w_rflag |= WFEDIT | WFMOVE;
320 	return (d_warpdot(curwp->w_dotp, &curwp->w_doto));
321 }
322 
323 /* ARGSUSED */
324 int
325 d_undelbak(int f, int n)
326 {
327 	if (n < 0)
328 		return (d_undel(f, -n));
329 	while (n--) {
330 		if (lback(curwp->w_dotp) != curbp->b_headp) {
331 			curwp->w_dotp = lback(curwp->w_dotp);
332 			curwp->w_dotline--;
333 		}
334 		if (llength(curwp->w_dotp) > 0)
335 			lputc(curwp->w_dotp, 0, ' ');
336 	}
337 	curwp->w_rflag |= WFEDIT | WFMOVE;
338 	return (d_warpdot(curwp->w_dotp, &curwp->w_doto));
339 }
340 
341 /* ARGSUSED */
342 int
343 d_findfile(int f, int n)
344 {
345 	struct buffer	*bp;
346 	int		 s;
347 	char		 fname[NFILEN];
348 
349 	if ((s = d_makename(curwp->w_dotp, fname, sizeof(fname))) == ABORT)
350 		return (FALSE);
351 	if (s == TRUE)
352 		bp = dired_(fname);
353 	else
354 		bp = findbuffer(fname);
355 	if (bp == NULL)
356 		return (FALSE);
357 	curbp = bp;
358 	if (showbuffer(bp, curwp, WFFULL) != TRUE)
359 		return (FALSE);
360 	if (bp->b_fname[0] != 0)
361 		return (TRUE);
362 	return (readin(fname));
363 }
364 
365 /* ARGSUSED */
366 int
367 d_ffotherwindow(int f, int n)
368 {
369 	char		 fname[NFILEN];
370 	int		 s;
371 	struct buffer	*bp;
372 	struct mgwin	*wp;
373 
374 	if ((s = d_makename(curwp->w_dotp, fname, sizeof(fname))) == ABORT)
375 		return (FALSE);
376 	if ((bp = (s ? dired_(fname) : findbuffer(fname))) == NULL)
377 		return (FALSE);
378 	if ((wp = popbuf(bp, WNONE)) == NULL)
379 		return (FALSE);
380 	curbp = bp;
381 	curwp = wp;
382 	if (bp->b_fname[0] != 0)
383 		return (TRUE);	/* never true for dired buffers */
384 	return (readin(fname));
385 }
386 
387 /* ARGSUSED */
388 int
389 d_expunge(int f, int n)
390 {
391 	struct line	*lp, *nlp;
392 	char		 fname[NFILEN], sname[NFILEN];
393 	int		 tmp;
394 
395 	tmp = curwp->w_dotline;
396 	curwp->w_dotline = 0;
397 
398 	for (lp = bfirstlp(curbp); lp != curbp->b_headp; lp = nlp) {
399 		curwp->w_dotline++;
400 		nlp = lforw(lp);
401 		if (llength(lp) && lgetc(lp, 0) == 'D') {
402 			switch (d_makename(lp, fname, sizeof(fname))) {
403 			case ABORT:
404 				dobeep();
405 				ewprintf("Bad line in dired buffer");
406 				curwp->w_dotline = tmp;
407 				return (FALSE);
408 			case FALSE:
409 				if (unlink(fname) < 0) {
410 					(void)xbasename(sname, fname, NFILEN);
411 					dobeep();
412 					ewprintf("Could not delete '%s'", sname);
413 					curwp->w_dotline = tmp;
414 					return (FALSE);
415 				}
416 				break;
417 			case TRUE:
418 				if (rmdir(fname) < 0) {
419 					(void)xbasename(sname, fname, NFILEN);
420 					dobeep();
421 					ewprintf("Could not delete directory "
422 					    "'%s'", sname);
423 					curwp->w_dotline = tmp;
424 					return (FALSE);
425 				}
426 				break;
427 			}
428 			lfree(lp);
429 			curwp->w_bufp->b_lines--;
430 			if (tmp > curwp->w_dotline)
431 				tmp--;
432 			curwp->w_rflag |= WFFULL;
433 		}
434 	}
435 	curwp->w_dotline = tmp;
436 	d_warpdot(curwp->w_dotp, &curwp->w_doto);
437 
438 	/* we have deleted all items successfully, remove del flag */
439 	curbp->b_flag &= ~BFDIREDDEL;
440 
441 	return (TRUE);
442 }
443 
444 /* ARGSUSED */
445 int
446 d_copy(int f, int n)
447 {
448 	char		 frname[NFILEN], toname[NFILEN], sname[NFILEN];
449 	char		*topath, *bufp;
450 	int		 ret;
451 	size_t		 off;
452 	struct buffer	*bp;
453 
454 	if (d_makename(curwp->w_dotp, frname, sizeof(frname)) != FALSE) {
455 		dobeep();
456 		ewprintf("Not a file");
457 		return (FALSE);
458 	}
459 	off = strlcpy(toname, curbp->b_fname, sizeof(toname));
460 	if (off >= sizeof(toname) - 1) {	/* can't happen, really */
461 		dobeep();
462 		ewprintf("Directory name too long");
463 		return (FALSE);
464 	}
465 	(void)xbasename(sname, frname, NFILEN);
466 	bufp = eread("Copy %s to: ", toname, sizeof(toname),
467 	    EFDEF | EFNEW | EFCR, sname);
468 	if (bufp == NULL)
469 		return (ABORT);
470 	else if (bufp[0] == '\0')
471 		return (FALSE);
472 
473 	topath = adjustname(toname, TRUE);
474 	ret = (copy(frname, topath) >= 0) ? TRUE : FALSE;
475 	if (ret != TRUE)
476 		return (ret);
477 	if ((bp = refreshbuffer(curbp)) == NULL)
478 		return (FALSE);
479 	return (showbuffer(bp, curwp, WFFULL | WFMODE));
480 }
481 
482 /* ARGSUSED */
483 int
484 d_rename(int f, int n)
485 {
486 	char		 frname[NFILEN], toname[NFILEN];
487 	char		*topath, *bufp;
488 	int		 ret;
489 	size_t		 off;
490 	struct buffer	*bp;
491 	char		 sname[NFILEN];
492 
493 	if (d_makename(curwp->w_dotp, frname, sizeof(frname)) != FALSE) {
494 		dobeep();
495 		ewprintf("Not a file");
496 		return (FALSE);
497 	}
498 	off = strlcpy(toname, curbp->b_fname, sizeof(toname));
499 	if (off >= sizeof(toname) - 1) {	/* can't happen, really */
500 		dobeep();
501 		ewprintf("Directory name too long");
502 		return (FALSE);
503 	}
504 	(void)xbasename(sname, frname, NFILEN);
505 	bufp = eread("Rename %s to: ", toname,
506 	    sizeof(toname), EFDEF | EFNEW | EFCR, sname);
507 	if (bufp == NULL)
508 		return (ABORT);
509 	else if (bufp[0] == '\0')
510 		return (FALSE);
511 
512 	topath = adjustname(toname, TRUE);
513 	ret = (rename(frname, topath) >= 0) ? TRUE : FALSE;
514 	if (ret != TRUE)
515 		return (ret);
516 	if ((bp = refreshbuffer(curbp)) == NULL)
517 		return (FALSE);
518 	return (showbuffer(bp, curwp, WFFULL | WFMODE));
519 }
520 
521 /* ARGSUSED */
522 void
523 reaper(int signo __attribute__((unused)))
524 {
525 	int	save_errno = errno, status;
526 
527 	while (waitpid(-1, &status, WNOHANG) >= 0)
528 		;
529 	errno = save_errno;
530 }
531 
532 /*
533  * Pipe the currently selected file through a shell command.
534  */
535 /* ARGSUSED */
536 int
537 d_shell_command(int f, int n)
538 {
539 	char		 command[512], fname[PATH_MAX], *bufp;
540 	struct buffer	*bp;
541 	struct mgwin	*wp;
542 	char		 sname[NFILEN];
543 
544 	bp = bfind("*Shell Command Output*", TRUE);
545 	if (bclear(bp) != TRUE)
546 		return (ABORT);
547 
548 	if (d_makename(curwp->w_dotp, fname, sizeof(fname)) != FALSE) {
549 		dobeep();
550 		ewprintf("bad line");
551 		return (ABORT);
552 	}
553 
554 	command[0] = '\0';
555 	(void)xbasename(sname, fname, NFILEN);
556 	bufp = eread("! on %s: ", command, sizeof(command), EFNEW, sname);
557 	if (bufp == NULL)
558 		return (ABORT);
559 
560 	if (d_exec(0, bp, fname, "sh", "-c", command, NULL) != TRUE)
561 		return (ABORT);
562 
563 	if ((wp = popbuf(bp, WNONE)) == NULL)
564 		return (ABORT);	/* XXX - free the buffer?? */
565 	curwp = wp;
566 	curbp = wp->w_bufp;
567 	return (TRUE);
568 }
569 
570 /*
571  * Pipe input file to cmd and insert the command's output in the
572  * given buffer.  Each line will be prefixed with the given
573  * number of spaces.
574  */
575 static int
576 d_exec(int space, struct buffer *bp, const char *input, const char *cmd, ...)
577 {
578 	char	 buf[BUFSIZ];
579 	va_list	 ap;
580 	struct	 sigaction olda, newa;
581 	char	**argv = NULL, *cp;
582 	FILE	*fin;
583 	int	 fds[2] = { -1, -1 };
584 	int	 infd = -1;
585 	int	 ret = (ABORT), n;
586 	pid_t	 pid;
587 
588 	if (sigaction(SIGCHLD, NULL, &olda) == -1)
589 		return (ABORT);
590 
591 	/* Find the number of arguments. */
592 	va_start(ap, cmd);
593 	for (n = 2; va_arg(ap, char *) != NULL; n++)
594 		;
595 	va_end(ap);
596 
597 	/* Allocate and build the argv. */
598 	if ((argv = calloc(n, sizeof(*argv))) == NULL) {
599 		dobeep();
600 		ewprintf("Can't allocate argv : %s", strerror(errno));
601 		goto out;
602 	}
603 
604 	n = 1;
605 	argv[0] = (char *)cmd;
606 	va_start(ap, cmd);
607 	while ((argv[n] = va_arg(ap, char *)) != NULL)
608 		n++;
609 	va_end(ap);
610 
611 	if (input == NULL)
612 		input = "/dev/null";
613 
614 	if ((infd = open(input, O_RDONLY)) == -1) {
615 		dobeep();
616 		ewprintf("Can't open input file : %s", strerror(errno));
617 		goto out;
618 	}
619 
620 	if (pipe(fds) == -1) {
621 		dobeep();
622 		ewprintf("Can't create pipe : %s", strerror(errno));
623 		goto out;
624 	}
625 
626 	newa.sa_handler = reaper;
627 	newa.sa_flags = 0;
628 	if (sigaction(SIGCHLD, &newa, NULL) == -1)
629 		goto out;
630 
631 	if ((pid = fork()) == -1) {
632 		dobeep();
633 		ewprintf("Can't fork");
634 		goto out;
635 	}
636 
637 	switch (pid) {
638 	case 0: /* Child */
639 		close(fds[0]);
640 		dup2(infd, STDIN_FILENO);
641 		dup2(fds[1], STDOUT_FILENO);
642 		dup2(fds[1], STDERR_FILENO);
643 		if (execvp(argv[0], argv) == -1)
644 			ewprintf("Can't exec %s: %s", argv[0], strerror(errno));
645 		exit(1);
646 		break;
647 	default: /* Parent */
648 		close(infd);
649 		close(fds[1]);
650 		infd = fds[1] = -1;
651 		if ((fin = fdopen(fds[0], "r")) == NULL)
652 			goto out;
653 		while (fgets(buf, sizeof(buf), fin) != NULL) {
654 			cp = strrchr(buf, '\n');
655 			if (cp == NULL && !feof(fin)) {	/* too long a line */
656 				int c;
657 				addlinef(bp, "%*s%s...", space, "", buf);
658 				while ((c = getc(fin)) != EOF && c != '\n')
659 					;
660 				continue;
661 			} else if (cp)
662 				*cp = '\0';
663 			addlinef(bp, "%*s%s", space, "", buf);
664 		}
665 		fclose(fin);
666 		break;
667 	}
668 	ret = (TRUE);
669 
670 out:
671 	if (sigaction(SIGCHLD, &olda, NULL) == -1)
672 		ewprintf("Warning, couldn't reset previous signal handler");
673 	if (fds[0] != -1)
674 		close(fds[0]);
675 	if (fds[1] != -1)
676 		close(fds[1]);
677 	if (infd != -1)
678 		close(infd);
679 	free(argv);
680 	return ret;
681 }
682 
683 /* ARGSUSED */
684 int
685 d_create_directory(int f, int n)
686 {
687 	int ret;
688 	struct buffer	*bp;
689 
690 	ret = ask_makedir();
691 	if (ret != TRUE)
692 		return(ret);
693 
694 	if ((bp = refreshbuffer(curbp)) == NULL)
695 		return (FALSE);
696 
697 	return (showbuffer(bp, curwp, WFFULL | WFMODE));
698 }
699 
700 /* ARGSUSED */
701 int
702 d_killbuffer_cmd(int f, int n)
703 {
704 	return(killbuffer_cmd(FFRAND, 0));
705 }
706 
707 int
708 d_refreshbuffer(int f, int n)
709 {
710 	struct buffer *bp;
711 
712 	if ((bp = refreshbuffer(curbp)) == NULL)
713 		return (FALSE);
714 
715 	return (showbuffer(bp, curwp, WFFULL | WFMODE));
716 }
717 
718 /*
719  * Kill then re-open the requested dired buffer.
720  * If required, take a note of any files marked for deletion. Then once
721  * the buffer has been re-opened, remark the same files as deleted.
722  */
723 struct buffer *
724 refreshbuffer(struct buffer *bp)
725 {
726 	char		*tmp_b_fname;
727 	int	 	 i, tmp_w_dotline, ddel = 0;
728 
729 	/* remember directory path to open later */
730 	tmp_b_fname = strdup(bp->b_fname);
731 	if (tmp_b_fname == NULL) {
732 		dobeep();
733 		ewprintf("Out of memory");
734 		return (NULL);
735 	}
736 	tmp_w_dotline = curwp->w_dotline;
737 
738 	/* create a list of files for deletion */
739 	if (bp->b_flag & BFDIREDDEL)
740 		ddel = createlist(bp);
741 
742 	killbuffer(bp);
743 
744 	/* dired_() uses findbuffer() to create new buffer */
745 	if ((bp = dired_(tmp_b_fname)) == NULL) {
746 		free(tmp_b_fname);
747 		return (NULL);
748 	}
749 	free(tmp_b_fname);
750 
751 	/* remark any previously deleted files with a 'D' */
752 	if (ddel)
753 		redelete(bp);
754 
755 	/* find dot line */
756 	bp->b_dotp = bfirstlp(bp);
757 	if (tmp_w_dotline > bp->b_lines)
758 		tmp_w_dotline = bp->b_lines - 1;
759 	for (i = 1; i < tmp_w_dotline; i++)
760 		bp->b_dotp = lforw(bp->b_dotp);
761 
762 	bp->b_dotline = i;
763 	bp->b_doto = 0;
764 	d_warpdot(bp->b_dotp, &bp->b_doto);
765 
766 	curbp = bp;
767 
768 	return (bp);
769 }
770 
771 static int
772 d_makename(struct line *lp, char *fn, size_t len)
773 {
774 	int	 start, nlen;
775 	char	*namep;
776 
777 	if (d_warpdot(lp, &start) == FALSE)
778 		return (ABORT);
779 	namep = &lp->l_text[start];
780 	nlen = llength(lp) - start;
781 
782 	if (snprintf(fn, len, "%s%.*s", curbp->b_fname, nlen, namep) >= len)
783 		return (ABORT); /* Name is too long. */
784 
785 	/* Return TRUE if the entry is a directory. */
786 	return ((lgetc(lp, 2) == 'd') ? TRUE : FALSE);
787 }
788 
789 #define NAME_FIELD	9
790 
791 static int
792 d_warpdot(struct line *dotp, int *doto)
793 {
794 	char *tp = dotp->l_text;
795 	int off = 0, field = 0, len;
796 
797 	/*
798 	 * Find the byte offset to the (space-delimited) filename
799 	 * field in formatted ls output.
800 	 */
801 	len = llength(dotp);
802 	while (off < len) {
803 		if (tp[off++] == ' ') {
804 			if (++field == NAME_FIELD) {
805 				*doto = off;
806 				return (TRUE);
807 			}
808 			/* Skip the space. */
809 			while (off < len && tp[off] == ' ')
810 				off++;
811 		}
812 	}
813 	/* We didn't find the field. */
814 	*doto = 0;
815 	return (FALSE);
816 }
817 
818 static int
819 d_forwpage(int f, int n)
820 {
821 	forwpage(f | FFRAND, n);
822 	return (d_warpdot(curwp->w_dotp, &curwp->w_doto));
823 }
824 
825 static int
826 d_backpage (int f, int n)
827 {
828 	backpage(f | FFRAND, n);
829 	return (d_warpdot(curwp->w_dotp, &curwp->w_doto));
830 }
831 
832 static int
833 d_forwline (int f, int n)
834 {
835 	forwline(f | FFRAND, n);
836 	return (d_warpdot(curwp->w_dotp, &curwp->w_doto));
837 }
838 
839 static int
840 d_backline (int f, int n)
841 {
842 	backline(f | FFRAND, n);
843 	return (d_warpdot(curwp->w_dotp, &curwp->w_doto));
844 }
845 
846 int
847 d_filevisitalt (int f, int n)
848 {
849 	char	 fname[NFILEN];
850 
851 	if (d_makename(curwp->w_dotp, fname, sizeof(fname)) == ABORT)
852 		return (FALSE);
853 
854 	return(do_filevisitalt(fname));
855 }
856 
857 /*
858  * XXX dname needs to have enough place to store an additional '/'.
859  */
860 struct buffer *
861 dired_(char *dname)
862 {
863 	struct buffer	*bp;
864 	int		 i;
865 	size_t		 len;
866 
867 	if ((dname = adjustname(dname, TRUE)) == NULL) {
868 		dobeep();
869 		ewprintf("Bad directory name");
870 		return (NULL);
871 	}
872 	/* this should not be done, instead adjustname() should get a flag */
873 	len = strlen(dname);
874 	if (dname[len - 1] != '/') {
875 		dname[len++] = '/';
876 		dname[len] = '\0';
877 	}
878 	if ((access(dname, R_OK | X_OK)) == -1) {
879 		if (errno == EACCES) {
880 			dobeep();
881 			ewprintf("Permission denied: %s", dname);
882 		}
883 		return (NULL);
884 	}
885 	for (bp = bheadp; bp != NULL; bp = bp->b_bufp) {
886 		if (strcmp(bp->b_fname, dname) == 0) {
887 			if (fchecktime(bp) != TRUE)
888 				ewprintf("Directory has changed on disk;"
889 				    " type g to update Dired");
890 			return (bp);
891 		}
892 
893 	}
894 	bp = bfind(dname, TRUE);
895 	bp->b_flag |= BFREADONLY | BFIGNDIRTY;
896 
897 	if ((d_exec(2, bp, NULL, "ls", "-al", dname, NULL)) != TRUE)
898 		return (NULL);
899 
900 	/* Find the line with ".." on it. */
901 	bp->b_dotp = bfirstlp(bp);
902 	bp->b_dotline = 1;
903 	for (i = 0; i < bp->b_lines; i++) {
904 		bp->b_dotp = lforw(bp->b_dotp);
905 		bp->b_dotline++;
906 		if (d_warpdot(bp->b_dotp, &bp->b_doto) == FALSE)
907 			continue;
908 		if (strcmp(ltext(bp->b_dotp) + bp->b_doto, "..") == 0)
909 			break;
910 	}
911 
912 	/* We want dot on the entry right after "..", if possible. */
913 	if (++i < bp->b_lines - 2) {
914 		bp->b_dotp = lforw(bp->b_dotp);
915 		bp->b_dotline++;
916 	}
917 	d_warpdot(bp->b_dotp, &bp->b_doto);
918 
919 	(void)strlcpy(bp->b_fname, dname, sizeof(bp->b_fname));
920 	(void)strlcpy(bp->b_cwd, dname, sizeof(bp->b_cwd));
921 	if ((bp->b_modes[1] = name_mode("dired")) == NULL) {
922 		bp->b_modes[0] = name_mode("fundamental");
923 		dobeep();
924 		ewprintf("Could not find mode dired");
925 		return (NULL);
926 	}
927 	(void)fupdstat(bp);
928 	bp->b_nmodes = 1;
929 	return (bp);
930 }
931 
932 /*
933  * Iterate through the lines of the dired buffer looking for files
934  * collected in the linked list made in createlist(). If a line is found
935  * replace 'D' as first char in a line. As lines are found, remove the
936  * corresponding item from the linked list. Iterate for as long as there
937  * are items in the linked list or until end of buffer is found.
938  */
939 void
940 redelete(struct buffer *bp)
941 {
942 	struct delentry	*d1 = NULL;
943 	struct line	*lp, *nlp;
944 	char		 fname[NFILEN];
945 	char		*p = fname;
946 	size_t		 plen, fnlen;
947 	int		 finished = 0;
948 
949 	/* reset the deleted file buffer flag until a deleted file is found */
950 	bp->b_flag &= ~BFDIREDDEL;
951 
952 	for (lp = bfirstlp(bp); lp != bp->b_headp; lp = nlp) {
953 		bp->b_dotp = lp;
954 		if ((p = findfname(lp, p)) == NULL) {
955 			nlp = lforw(lp);
956 			continue;
957 		}
958 		plen = strlen(p);
959 		SLIST_FOREACH(d1, &delhead, entry) {
960 			fnlen = strlen(d1->fn);
961 			if ((plen == fnlen) &&
962 			    (strncmp(p, d1->fn, plen) == 0)) {
963 				lputc(bp->b_dotp, 0, DDELCHAR);
964 				bp->b_flag |= BFDIREDDEL;
965 				SLIST_REMOVE(&delhead, d1, delentry, entry);
966 				if (SLIST_EMPTY(&delhead)) {
967 					finished = 1;
968 					break;
969 				}
970 			}
971 		}
972 		if (finished)
973 			break;
974 		nlp = lforw(lp);
975 	}
976 	while (!SLIST_EMPTY(&delhead)) {
977 		d1 = SLIST_FIRST(&delhead);
978 		SLIST_REMOVE_HEAD(&delhead, entry);
979 		free(d1->fn);
980 		free(d1);
981 	}
982 	return;
983 }
984 
985 /*
986  * Create a list of files marked for deletion.
987  */
988 int
989 createlist(struct buffer *bp)
990 {
991 	struct delentry	*d1 = NULL, *d2;
992 	struct line	*lp, *nlp;
993 	char		 fname[NFILEN];
994 	char		*p = fname;
995 	int		 ret = FALSE;
996 
997 	for (lp = bfirstlp(bp); lp != bp->b_headp; lp = nlp) {
998 		/*
999 		 * Check if the line has 'D' on the first char and if a valid
1000 		 * filename can be extracted from it.
1001 		 */
1002 		if (((lp->l_text[0] != DDELCHAR)) ||
1003 		    ((p = findfname(lp, p)) == NULL)) {
1004 			nlp = lforw(lp);
1005 			continue;
1006 		}
1007 		if (SLIST_EMPTY(&delhead)) {
1008 			if ((d1 = malloc(sizeof(struct delentry)))
1009 			     == NULL)
1010 				return (ABORT);
1011 			if ((d1->fn = strdup(p)) == NULL) {
1012 				free(d1);
1013 				return (ABORT);
1014 			}
1015 			SLIST_INSERT_HEAD(&delhead, d1, entry);
1016 		} else {
1017 			if ((d2 = malloc(sizeof(struct delentry)))
1018 			     == NULL) {
1019 				free(d1->fn);
1020 				free(d1);
1021 				return (ABORT);
1022 			}
1023 			if ((d2->fn = strdup(p)) == NULL) {
1024 				free(d1->fn);
1025 				free(d1);
1026 				free(d2);
1027 				return (ABORT);
1028 			}
1029 			SLIST_INSERT_AFTER(d1, d2, entry);
1030 			d1 = d2;
1031 		}
1032 		ret = TRUE;
1033 		nlp = lforw(lp);
1034 	}
1035 	return (ret);
1036 }
1037 
1038 /*
1039  * Look for and extract a file name on a dired buffer line.
1040  */
1041 char *
1042 findfname(struct line *lp, char *fn)
1043 {
1044 	int start;
1045 
1046 	(void)d_warpdot(lp, &start);
1047 	if (start < 1)
1048 		return NULL;
1049 	fn = &lp->l_text[start];
1050 	return fn;
1051 }
1052