xref: /openbsd-src/usr.bin/mg/dired.c (revision de8cc8edbc71bd3e3bc7fbffa27ba0e564c37d8b)
1 /*	$OpenBSD: dired.c,v 1.97 2021/03/01 10:51:14 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 			ret = snprintf(toname, sizeof(toname), "%s/%s",
483 			    topath, sname);
484 			if (ret < 0 || ret >= sizeof(toname) - 1) {
485 				dobeep();
486 				ewprintf("Directory name too long");
487 				return (FALSE);
488 			}
489 			topath = adjustname(toname, TRUE);
490 		}
491 	}
492 	if (topath == NULL)
493 		return (FALSE);
494 	if (strcmp(frname, topath) == 0) {
495 		ewprintf("Cannot copy to same file: %s", frname);
496 		return (TRUE);
497 	}
498 	ret = (copy(frname, topath) >= 0) ? TRUE : FALSE;
499 	if (ret != TRUE)
500 		return (ret);
501 	if ((bp = refreshbuffer(curbp)) == NULL)
502 		return (FALSE);
503 
504 	ewprintf("Copy: 1 file");
505 	return (showbuffer(bp, curwp, WFFULL | WFMODE));
506 }
507 
508 /* ARGSUSED */
509 int
510 d_rename(int f, int n)
511 {
512 	struct stat      statbuf;
513 	char		 frname[NFILEN], toname[NFILEN];
514 	char		*topath, *bufp;
515 	int		 ret;
516 	size_t		 off;
517 	struct buffer	*bp;
518 	char		 sname[NFILEN];
519 
520 	if (d_makename(curwp->w_dotp, frname, sizeof(frname)) != FALSE) {
521 		dobeep();
522 		ewprintf("Not a file");
523 		return (FALSE);
524 	}
525 	off = strlcpy(toname, curbp->b_fname, sizeof(toname));
526 	if (off >= sizeof(toname) - 1) {	/* can't happen, really */
527 		dobeep();
528 		ewprintf("Name too long");
529 		return (FALSE);
530 	}
531 	(void)xbasename(sname, frname, NFILEN);
532 	bufp = eread("Rename %s to: ", toname,
533 	    sizeof(toname), EFDEF | EFNEW | EFCR, sname);
534 	if (bufp == NULL)
535 		return (ABORT);
536 	else if (bufp[0] == '\0')
537 		return (FALSE);
538 
539 	topath = adjustname(toname, TRUE);
540 	if (stat(topath, &statbuf) == 0) {
541 		if (S_ISDIR(statbuf.st_mode)) {
542 			ret = snprintf(toname, sizeof(toname), "%s/%s",
543 			    topath, sname);
544 			if (ret < 0 || ret >= sizeof(toname) - 1) {
545 				dobeep();
546 				ewprintf("Directory name too long");
547 				return (FALSE);
548 			}
549 			topath = adjustname(toname, TRUE);
550 		}
551 	}
552 	if (topath == NULL)
553 		return (FALSE);
554 	if (strcmp(frname, topath) == 0) {
555 		ewprintf("Cannot move to same file: %s", frname);
556 		return (TRUE);
557 	}
558 	ret = (rename(frname, topath) >= 0) ? TRUE : FALSE;
559 	if (ret != TRUE)
560 		return (ret);
561 	if ((bp = refreshbuffer(curbp)) == NULL)
562 		return (FALSE);
563 
564 	ewprintf("Move: 1 file");
565 	return (showbuffer(bp, curwp, WFFULL | WFMODE));
566 }
567 
568 /* ARGSUSED */
569 void
570 reaper(int signo __attribute__((unused)))
571 {
572 	int	save_errno = errno, status;
573 
574 	while (waitpid(-1, &status, WNOHANG) >= 0)
575 		;
576 	errno = save_errno;
577 }
578 
579 /*
580  * Pipe the currently selected file through a shell command.
581  */
582 /* ARGSUSED */
583 int
584 d_shell_command(int f, int n)
585 {
586 	char		 command[512], fname[PATH_MAX], *bufp;
587 	struct buffer	*bp;
588 	struct mgwin	*wp;
589 	char		 sname[NFILEN];
590 
591 	bp = bfind("*Shell Command Output*", TRUE);
592 	if (bclear(bp) != TRUE)
593 		return (ABORT);
594 
595 	if (d_makename(curwp->w_dotp, fname, sizeof(fname)) != FALSE) {
596 		dobeep();
597 		ewprintf("bad line");
598 		return (ABORT);
599 	}
600 
601 	command[0] = '\0';
602 	(void)xbasename(sname, fname, NFILEN);
603 	bufp = eread("! on %s: ", command, sizeof(command), EFNEW, sname);
604 	if (bufp == NULL)
605 		return (ABORT);
606 
607 	if (d_exec(0, bp, fname, "sh", "-c", command, NULL) != TRUE)
608 		return (ABORT);
609 
610 	if ((wp = popbuf(bp, WNONE)) == NULL)
611 		return (ABORT);	/* XXX - free the buffer?? */
612 	curwp = wp;
613 	curbp = wp->w_bufp;
614 	return (TRUE);
615 }
616 
617 /*
618  * Pipe input file to cmd and insert the command's output in the
619  * given buffer.  Each line will be prefixed with the given
620  * number of spaces.
621  */
622 static int
623 d_exec(int space, struct buffer *bp, const char *input, const char *cmd, ...)
624 {
625 	char	 buf[BUFSIZ];
626 	va_list	 ap;
627 	struct	 sigaction olda, newa;
628 	char	**argv = NULL, *cp;
629 	FILE	*fin;
630 	int	 fds[2] = { -1, -1 };
631 	int	 infd = -1;
632 	int	 ret = (ABORT), n;
633 	pid_t	 pid;
634 
635 	if (sigaction(SIGCHLD, NULL, &olda) == -1)
636 		return (ABORT);
637 
638 	/* Find the number of arguments. */
639 	va_start(ap, cmd);
640 	for (n = 2; va_arg(ap, char *) != NULL; n++)
641 		;
642 	va_end(ap);
643 
644 	/* Allocate and build the argv. */
645 	if ((argv = calloc(n, sizeof(*argv))) == NULL) {
646 		dobeep();
647 		ewprintf("Can't allocate argv : %s", strerror(errno));
648 		goto out;
649 	}
650 
651 	n = 1;
652 	argv[0] = (char *)cmd;
653 	va_start(ap, cmd);
654 	while ((argv[n] = va_arg(ap, char *)) != NULL)
655 		n++;
656 	va_end(ap);
657 
658 	if (input == NULL)
659 		input = "/dev/null";
660 
661 	if ((infd = open(input, O_RDONLY)) == -1) {
662 		dobeep();
663 		ewprintf("Can't open input file : %s", strerror(errno));
664 		goto out;
665 	}
666 
667 	if (pipe(fds) == -1) {
668 		dobeep();
669 		ewprintf("Can't create pipe : %s", strerror(errno));
670 		goto out;
671 	}
672 
673 	newa.sa_handler = reaper;
674 	newa.sa_flags = 0;
675 	if (sigaction(SIGCHLD, &newa, NULL) == -1)
676 		goto out;
677 
678 	if ((pid = fork()) == -1) {
679 		dobeep();
680 		ewprintf("Can't fork");
681 		goto out;
682 	}
683 
684 	switch (pid) {
685 	case 0: /* Child */
686 		close(fds[0]);
687 		dup2(infd, STDIN_FILENO);
688 		dup2(fds[1], STDOUT_FILENO);
689 		dup2(fds[1], STDERR_FILENO);
690 		if (execvp(argv[0], argv) == -1)
691 			ewprintf("Can't exec %s: %s", argv[0], strerror(errno));
692 		exit(1);
693 		break;
694 	default: /* Parent */
695 		close(infd);
696 		close(fds[1]);
697 		infd = fds[1] = -1;
698 		if ((fin = fdopen(fds[0], "r")) == NULL)
699 			goto out;
700 		while (fgets(buf, sizeof(buf), fin) != NULL) {
701 			cp = strrchr(buf, *bp->b_nlchr);
702 			if (cp == NULL && !feof(fin)) {	/* too long a line */
703 				int c;
704 				addlinef(bp, "%*s%s...", space, "", buf);
705 				while ((c = getc(fin)) != EOF &&
706 				    c != *bp->b_nlchr)
707 					;
708 				continue;
709 			} else if (cp)
710 				*cp = '\0';
711 			addlinef(bp, "%*s%s", space, "", buf);
712 		}
713 		fclose(fin);
714 		break;
715 	}
716 	ret = (TRUE);
717 
718 out:
719 	if (sigaction(SIGCHLD, &olda, NULL) == -1)
720 		ewprintf("Warning, couldn't reset previous signal handler");
721 	if (fds[0] != -1)
722 		close(fds[0]);
723 	if (fds[1] != -1)
724 		close(fds[1]);
725 	if (infd != -1)
726 		close(infd);
727 	free(argv);
728 	return ret;
729 }
730 
731 /* ARGSUSED */
732 int
733 d_create_directory(int f, int n)
734 {
735 	int ret;
736 	struct buffer	*bp;
737 
738 	ret = ask_makedir();
739 	if (ret != TRUE)
740 		return(ret);
741 
742 	if ((bp = refreshbuffer(curbp)) == NULL)
743 		return (FALSE);
744 
745 	return (showbuffer(bp, curwp, WFFULL | WFMODE));
746 }
747 
748 /* ARGSUSED */
749 int
750 d_killbuffer_cmd(int f, int n)
751 {
752 	return(killbuffer_cmd(FFRAND, 0));
753 }
754 
755 int
756 d_refreshbuffer(int f, int n)
757 {
758 	struct buffer *bp;
759 
760 	if ((bp = refreshbuffer(curbp)) == NULL)
761 		return (FALSE);
762 
763 	return (showbuffer(bp, curwp, WFFULL | WFMODE));
764 }
765 
766 /*
767  * Kill then re-open the requested dired buffer.
768  * If required, take a note of any files marked for deletion. Then once
769  * the buffer has been re-opened, remark the same files as deleted.
770  */
771 struct buffer *
772 refreshbuffer(struct buffer *bp)
773 {
774 	char		*tmp_b_fname;
775 	int	 	 i, tmp_w_dotline, ddel = 0;
776 
777 	/* remember directory path to open later */
778 	tmp_b_fname = strdup(bp->b_fname);
779 	if (tmp_b_fname == NULL) {
780 		dobeep();
781 		ewprintf("Out of memory");
782 		return (NULL);
783 	}
784 	tmp_w_dotline = curwp->w_dotline;
785 
786 	/* create a list of files for deletion */
787 	if (bp->b_flag & BFDIREDDEL)
788 		ddel = createlist(bp);
789 
790 	killbuffer(bp);
791 
792 	/* dired_() uses findbuffer() to create new buffer */
793 	if ((bp = dired_(tmp_b_fname)) == NULL) {
794 		free(tmp_b_fname);
795 		return (NULL);
796 	}
797 	free(tmp_b_fname);
798 
799 	/* remark any previously deleted files with a 'D' */
800 	if (ddel)
801 		redelete(bp);
802 
803 	/* find dot line */
804 	bp->b_dotp = bfirstlp(bp);
805 	if (tmp_w_dotline > bp->b_lines)
806 		tmp_w_dotline = bp->b_lines - 1;
807 	for (i = 1; i < tmp_w_dotline; i++)
808 		bp->b_dotp = lforw(bp->b_dotp);
809 
810 	bp->b_dotline = i;
811 	bp->b_doto = 0;
812 	d_warpdot(bp->b_dotp, &bp->b_doto);
813 
814 	curbp = bp;
815 
816 	return (bp);
817 }
818 
819 static int
820 d_makename(struct line *lp, char *fn, size_t len)
821 {
822 	int	 start, nlen, ret;
823 	char	*namep;
824 
825 	if (d_warpdot(lp, &start) == FALSE)
826 		return (ABORT);
827 	namep = &lp->l_text[start];
828 	nlen = llength(lp) - start;
829 
830 	ret = snprintf(fn, len, "%s%.*s", curbp->b_fname, nlen, namep);
831 	if (ret < 0 || ret >= (int)len)
832 		return (ABORT); /* Name is too long. */
833 
834 	/* Return TRUE if the entry is a directory. */
835 	return ((lgetc(lp, 2) == 'd') ? TRUE : FALSE);
836 }
837 
838 #define NAME_FIELD	9
839 
840 static int
841 d_warpdot(struct line *dotp, int *doto)
842 {
843 	char *tp = dotp->l_text;
844 	int off = 0, field = 0, len;
845 
846 	/*
847 	 * Find the byte offset to the (space-delimited) filename
848 	 * field in formatted ls output.
849 	 */
850 	len = llength(dotp);
851 	while (off < len) {
852 		if (tp[off++] == ' ') {
853 			if (++field == NAME_FIELD) {
854 				*doto = off;
855 				return (TRUE);
856 			}
857 			/* Skip the space. */
858 			while (off < len && tp[off] == ' ')
859 				off++;
860 		}
861 	}
862 	/* We didn't find the field. */
863 	*doto = 0;
864 	return (FALSE);
865 }
866 
867 static int
868 d_forwpage(int f, int n)
869 {
870 	forwpage(f | FFRAND, n);
871 	return (d_warpdot(curwp->w_dotp, &curwp->w_doto));
872 }
873 
874 static int
875 d_backpage (int f, int n)
876 {
877 	backpage(f | FFRAND, n);
878 	return (d_warpdot(curwp->w_dotp, &curwp->w_doto));
879 }
880 
881 static int
882 d_forwline (int f, int n)
883 {
884 	forwline(f | FFRAND, n);
885 	return (d_warpdot(curwp->w_dotp, &curwp->w_doto));
886 }
887 
888 static int
889 d_backline (int f, int n)
890 {
891 	backline(f | FFRAND, n);
892 	return (d_warpdot(curwp->w_dotp, &curwp->w_doto));
893 }
894 
895 int
896 d_filevisitalt (int f, int n)
897 {
898 	char	 fname[NFILEN];
899 
900 	if (d_makename(curwp->w_dotp, fname, sizeof(fname)) == ABORT)
901 		return (FALSE);
902 
903 	return(do_filevisitalt(fname));
904 }
905 
906 /*
907  * XXX dname needs to have enough place to store an additional '/'.
908  */
909 struct buffer *
910 dired_(char *dname)
911 {
912 	struct buffer	*bp;
913 	int		 i;
914 	size_t		 len;
915 
916 	if ((dname = adjustname(dname, TRUE)) == NULL) {
917 		dobeep();
918 		ewprintf("Bad directory name");
919 		return (NULL);
920 	}
921 	/* this should not be done, instead adjustname() should get a flag */
922 	len = strlen(dname);
923 	if (dname[len - 1] != '/') {
924 		dname[len++] = '/';
925 		dname[len] = '\0';
926 	}
927 	if ((access(dname, R_OK | X_OK)) == -1) {
928 		if (errno == EACCES) {
929 			dobeep();
930 			ewprintf("Permission denied: %s", dname);
931 		}
932 		return (NULL);
933 	}
934 	for (bp = bheadp; bp != NULL; bp = bp->b_bufp) {
935 		if (strcmp(bp->b_fname, dname) == 0) {
936 			if (fchecktime(bp) != TRUE)
937 				ewprintf("Directory has changed on disk;"
938 				    " type g to update Dired");
939 			return (bp);
940 		}
941 
942 	}
943 	bp = bfind(dname, TRUE);
944 	bp->b_flag |= BFREADONLY | BFIGNDIRTY;
945 
946 	if ((d_exec(2, bp, NULL, "ls", "-al", dname, NULL)) != TRUE)
947 		return (NULL);
948 
949 	/* Find the line with ".." on it. */
950 	bp->b_dotp = bfirstlp(bp);
951 	bp->b_dotline = 1;
952 	for (i = 0; i < bp->b_lines; i++) {
953 		bp->b_dotp = lforw(bp->b_dotp);
954 		bp->b_dotline++;
955 		if (d_warpdot(bp->b_dotp, &bp->b_doto) == FALSE)
956 			continue;
957 		if (strcmp(ltext(bp->b_dotp) + bp->b_doto, "..") == 0)
958 			break;
959 	}
960 
961 	/* We want dot on the entry right after "..", if possible. */
962 	if (++i < bp->b_lines - 2) {
963 		bp->b_dotp = lforw(bp->b_dotp);
964 		bp->b_dotline++;
965 	}
966 	d_warpdot(bp->b_dotp, &bp->b_doto);
967 
968 	(void)strlcpy(bp->b_fname, dname, sizeof(bp->b_fname));
969 	(void)strlcpy(bp->b_cwd, dname, sizeof(bp->b_cwd));
970 	if ((bp->b_modes[1] = name_mode("dired")) == NULL) {
971 		bp->b_modes[0] = name_mode("fundamental");
972 		dobeep();
973 		ewprintf("Could not find mode dired");
974 		return (NULL);
975 	}
976 	(void)fupdstat(bp);
977 	bp->b_nmodes = 1;
978 	return (bp);
979 }
980 
981 /*
982  * Iterate through the lines of the dired buffer looking for files
983  * collected in the linked list made in createlist(). If a line is found
984  * replace 'D' as first char in a line. As lines are found, remove the
985  * corresponding item from the linked list. Iterate for as long as there
986  * are items in the linked list or until end of buffer is found.
987  */
988 void
989 redelete(struct buffer *bp)
990 {
991 	struct delentry	*dt, *d1 = NULL;
992 	struct line	*lp, *nlp;
993 	char		 fname[NFILEN];
994 	char		*p = fname;
995 	size_t		 plen, fnlen;
996 	int		 finished = 0;
997 
998 	/* reset the deleted file buffer flag until a deleted file is found */
999 	bp->b_flag &= ~BFDIREDDEL;
1000 
1001 	for (lp = bfirstlp(bp); lp != bp->b_headp; lp = nlp) {
1002 		bp->b_dotp = lp;
1003 		if ((p = findfname(lp, p)) == NULL) {
1004 			nlp = lforw(lp);
1005 			continue;
1006 		}
1007 		plen = strlen(p);
1008 		SLIST_FOREACH_SAFE(d1, &delhead, entry, dt) {
1009 			fnlen = strlen(d1->fn);
1010 			if ((plen == fnlen) &&
1011 			    (strncmp(p, d1->fn, plen) == 0)) {
1012 				lputc(bp->b_dotp, 0, DDELCHAR);
1013 				bp->b_flag |= BFDIREDDEL;
1014 				SLIST_REMOVE(&delhead, d1, delentry, entry);
1015 				if (SLIST_EMPTY(&delhead)) {
1016 					finished = 1;
1017 					break;
1018 				}
1019 			}
1020 		}
1021 		if (finished)
1022 			break;
1023 		nlp = lforw(lp);
1024 	}
1025 	while (!SLIST_EMPTY(&delhead)) {
1026 		d1 = SLIST_FIRST(&delhead);
1027 		SLIST_REMOVE_HEAD(&delhead, entry);
1028 		free(d1->fn);
1029 		free(d1);
1030 	}
1031 	return;
1032 }
1033 
1034 /*
1035  * Create a list of files marked for deletion.
1036  */
1037 int
1038 createlist(struct buffer *bp)
1039 {
1040 	struct delentry	*d1 = NULL, *d2;
1041 	struct line	*lp, *nlp;
1042 	char		 fname[NFILEN];
1043 	char		*p = fname;
1044 	int		 ret = FALSE;
1045 
1046 	for (lp = bfirstlp(bp); lp != bp->b_headp; lp = nlp) {
1047 		/*
1048 		 * Check if the line has 'D' on the first char and if a valid
1049 		 * filename can be extracted from it.
1050 		 */
1051 		if (((lp->l_text[0] != DDELCHAR)) ||
1052 		    ((p = findfname(lp, p)) == NULL)) {
1053 			nlp = lforw(lp);
1054 			continue;
1055 		}
1056 		if (SLIST_EMPTY(&delhead)) {
1057 			if ((d1 = malloc(sizeof(struct delentry)))
1058 			     == NULL)
1059 				return (ABORT);
1060 			if ((d1->fn = strdup(p)) == NULL) {
1061 				free(d1);
1062 				return (ABORT);
1063 			}
1064 			SLIST_INSERT_HEAD(&delhead, d1, entry);
1065 		} else {
1066 			if ((d2 = malloc(sizeof(struct delentry)))
1067 			     == NULL) {
1068 				free(d1->fn);
1069 				free(d1);
1070 				return (ABORT);
1071 			}
1072 			if ((d2->fn = strdup(p)) == NULL) {
1073 				free(d1->fn);
1074 				free(d1);
1075 				free(d2);
1076 				return (ABORT);
1077 			}
1078 			if (!d1)
1079 				SLIST_INSERT_HEAD(&delhead, d2, entry);
1080 			else
1081 				SLIST_INSERT_AFTER(d1, d2, entry);
1082 			d1 = d2;
1083 		}
1084 		ret = TRUE;
1085 		nlp = lforw(lp);
1086 	}
1087 	return (ret);
1088 }
1089 
1090 int
1091 d_gotofile(int f, int n)
1092 {
1093 	struct line	*lp, *nlp;
1094 	size_t		 lenfpath;
1095 	char		 fpath[NFILEN], fname[NFILEN];
1096 	char		*p, *fpth, *fnp = NULL;
1097 	int		 tmp;
1098 
1099 	if (getbufcwd(fpath, sizeof(fpath)) != TRUE)
1100 		fpath[0] = '\0';
1101 	lenfpath = strlen(fpath);
1102 	fnp = eread("Goto file: ", fpath, NFILEN,
1103 	    EFNEW | EFCR | EFFILE | EFDEF);
1104 	if (fnp == NULL)
1105 		return (ABORT);
1106 	else if (fnp[0] == '\0')
1107 		return (FALSE);
1108 
1109 	fpth = adjustname(fpath, TRUE);		/* Removes last '/' if dir...  */
1110 	if (fpth == NULL || strlen(fpth) == lenfpath - 1) { /* ...hence -1.    */
1111 		ewprintf("No file to find");	/* Current directory given so  */
1112 		return (TRUE);			/* return at present location. */
1113 	}
1114 	(void)xbasename(fname, fpth, NFILEN);
1115 	curbp = curwp->w_bufp;
1116 	tmp = 0;
1117 	for (lp = bfirstlp(curbp); lp != curbp->b_headp; lp = nlp) {
1118 		tmp++;
1119 		if ((p = findfname(lp, p)) == NULL) {
1120 			nlp = lforw(lp);
1121 			continue;
1122 		}
1123 		if (strcmp(fname, p) == 0) {
1124 			curwp->w_dotp = lp;
1125 			curwp->w_dotline = tmp;
1126 			(void)d_warpdot(curwp->w_dotp, &curwp->w_doto);
1127 			tmp--;
1128 			break;
1129 		}
1130 		nlp = lforw(lp);
1131 	}
1132 	if (tmp == curbp->b_lines - 1) {
1133 		ewprintf("File not found %s", fname);
1134 		return (FALSE);
1135 	} else {
1136 		ewprintf("");
1137 		return (TRUE);
1138 	}
1139 }
1140 
1141 /*
1142  * Look for and extract a file name on a dired buffer line.
1143  */
1144 char *
1145 findfname(struct line *lp, char *fn)
1146 {
1147 	int start;
1148 
1149 	(void)d_warpdot(lp, &start);
1150 	if (start < 1)
1151 		return NULL;
1152 	fn = &lp->l_text[start];
1153 	return fn;
1154 }
1155