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