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