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