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