xref: /openbsd-src/usr.bin/mg/dired.c (revision d13be5d47e4149db2549a9828e244d59dbc43f15)
1 /*	$OpenBSD: dired.c,v 1.50 2011/08/31 03:40:53 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 "def.h"
10 #include "funmap.h"
11 #include "kbd.h"
12 
13 #include <sys/types.h>
14 #include <sys/stat.h>
15 #include <sys/time.h>
16 #include <sys/resource.h>
17 #include <sys/wait.h>
18 
19 #include <ctype.h>
20 #include <signal.h>
21 #include <fcntl.h>
22 #include <errno.h>
23 #include <libgen.h>
24 #include <stdarg.h>
25 
26 void		 dired_init(void);
27 static int	 dired(int, int);
28 static int	 d_otherwindow(int, int);
29 static int	 d_undel(int, int);
30 static int	 d_undelbak(int, int);
31 static int	 d_findfile(int, int);
32 static int	 d_ffotherwindow(int, int);
33 static int	 d_expunge(int, int);
34 static int	 d_copy(int, int);
35 static int	 d_del(int, int);
36 static int	 d_rename(int, int);
37 static int	 d_exec(int, struct buffer *, const char *, const char *, ...);
38 static int	 d_shell_command(int, int);
39 static int	 d_create_directory(int, int);
40 static int	 d_makename(struct line *, char *, size_t);
41 static int	 d_warpdot(struct line *, int *);
42 static int	 d_forwpage(int, int);
43 static int	 d_backpage(int, int);
44 static int	 d_forwline(int, int);
45 static int	 d_backline(int, int);
46 static void	 reaper(int);
47 
48 extern struct keymap_s helpmap, cXmap, metamap;
49 
50 static PF dirednul[] = {
51 	setmark,		/* ^@ */
52 	gotobol,		/* ^A */
53 	backchar,		/* ^B */
54 	rescan,			/* ^C */
55 	d_del,			/* ^D */
56 	gotoeol,		/* ^E */
57 	forwchar,		/* ^F */
58 	ctrlg,			/* ^G */
59 #ifndef NO_HELP
60 	NULL,			/* ^H */
61 #endif /* !NO_HELP */
62 };
63 
64 static PF diredcl[] = {
65 	reposition,		/* ^L */
66 	d_findfile,		/* ^M */
67 	d_forwline,		/* ^N */
68 	rescan,			/* ^O */
69 	d_backline,		/* ^P */
70 	rescan,			/* ^Q */
71 	backisearch,		/* ^R */
72 	forwisearch,		/* ^S */
73 	rescan,			/* ^T */
74 	universal_argument,	/* ^U */
75 	d_forwpage,		/* ^V */
76 	rescan,			/* ^W */
77 	NULL			/* ^X */
78 };
79 
80 static PF diredcz[] = {
81 	spawncli,		/* ^Z */
82 	NULL,			/* esc */
83 	rescan,			/* ^\ */
84 	rescan,			/* ^] */
85 	rescan,			/* ^^ */
86 	rescan,			/* ^_ */
87 	d_forwline,		/* SP */
88 	d_shell_command,	/* ! */
89 	rescan,			/* " */
90 	rescan,			/* # */
91 	rescan,			/* $ */
92 	rescan,			/* % */
93 	rescan,			/* & */
94 	rescan,			/* ' */
95 	rescan,			/* ( */
96 	rescan,			/* ) */
97 	rescan,			/* * */
98 	d_create_directory	/* + */
99 };
100 
101 static PF diredc[] = {
102 	d_copy,			/* c */
103 	d_del,			/* d */
104 	d_findfile,		/* e */
105 	d_findfile		/* f */
106 };
107 
108 static PF diredn[] = {
109 	d_forwline,		/* n */
110 	d_ffotherwindow,	/* o */
111 	d_backline,		/* p */
112 	rescan,			/* q */
113 	d_rename,		/* r */
114 	rescan,			/* s */
115 	rescan,			/* t */
116 	d_undel,		/* u */
117 	rescan,			/* v */
118 	rescan,			/* w */
119 	d_expunge		/* x */
120 };
121 
122 static PF direddl[] = {
123 	d_undelbak		/* del */
124 };
125 
126 static PF diredbp[] = {
127 	d_backpage		/* v */
128 };
129 
130 static PF dirednull[] = {
131 	NULL
132 };
133 
134 #ifndef	DIRED_XMAPS
135 #define	NDIRED_XMAPS	0	/* number of extra map sections */
136 #endif /* DIRED_XMAPS */
137 
138 static struct KEYMAPE (1 + IMAPEXT) d_backpagemap = {
139 	1,
140 	1 + IMAPEXT,
141 	rescan,
142 	{
143 		{
144 		'v', 'v', diredbp, NULL
145 		}
146 	}
147 };
148 
149 static struct KEYMAPE (7 + NDIRED_XMAPS + IMAPEXT) diredmap = {
150 	7 + NDIRED_XMAPS,
151 	7 + NDIRED_XMAPS + IMAPEXT,
152 	rescan,
153 	{
154 #ifndef NO_HELP
155 		{
156 			CCHR('@'), CCHR('H'), dirednul, (KEYMAP *) & helpmap
157 		},
158 #else /* !NO_HELP */
159 		{
160 			CCHR('@'), CCHR('G'), dirednul, NULL
161 		},
162 #endif /* !NO_HELP */
163 		{
164 			CCHR('L'), CCHR('X'), diredcl, (KEYMAP *) & cXmap
165 		},
166 		{
167 			CCHR('['), CCHR('['), dirednull, (KEYMAP *) &
168 			d_backpagemap
169 		},
170 		{
171 			CCHR('Z'), '+', diredcz, (KEYMAP *) & metamap
172 		},
173 		{
174 			'c', 'f', diredc, NULL
175 		},
176 		{
177 			'n', 'x', diredn, NULL
178 		},
179 		{
180 			CCHR('?'), CCHR('?'), direddl, NULL
181 		},
182 #ifdef	DIRED_XMAPS
183 		DIRED_XMAPS,	/* map sections for dired mode keys	 */
184 #endif /* DIRED_XMAPS */
185 	}
186 };
187 
188 void
189 dired_init(void)
190 {
191 	funmap_add(dired, "dired");
192 	funmap_add(d_undelbak, "dired-backup-unflag");
193 	funmap_add(d_copy, "dired-copy-file");
194 	funmap_add(d_expunge, "dired-do-deletions");
195 	funmap_add(d_findfile, "dired-find-file");
196 	funmap_add(d_ffotherwindow, "dired-find-file-other-window");
197 	funmap_add(d_del, "dired-flag-file-deleted");
198 	funmap_add(d_forwline, "dired-next-line");
199 	funmap_add(d_otherwindow, "dired-other-window");
200 	funmap_add(d_backline, "dired-previous-line");
201 	funmap_add(d_rename, "dired-rename-file");
202 	funmap_add(d_backpage, "dired-scroll-down");
203 	funmap_add(d_forwpage, "dired-scroll-up");
204 	funmap_add(d_undel, "dired-unflag");
205 	maps_add((KEYMAP *)&diredmap, "dired");
206 	dobindkey(fundamental_map, "dired", "^Xd");
207 }
208 
209 /* ARGSUSED */
210 int
211 dired(int f, int n)
212 {
213 	char		 dname[NFILEN], *bufp, *slash;
214 	struct buffer	*bp;
215 
216 	if (curbp->b_fname && curbp->b_fname[0] != '\0') {
217 		(void)strlcpy(dname, curbp->b_fname, sizeof(dname));
218 		if ((slash = strrchr(dname, '/')) != NULL) {
219 			*(slash + 1) = '\0';
220 		}
221 	} else {
222 		if (getcwd(dname, sizeof(dname)) == NULL)
223 			dname[0] = '\0';
224 	}
225 
226 	if ((bufp = eread("Dired: ", dname, NFILEN,
227 	    EFDEF | EFNEW | EFCR)) == NULL)
228 		return (ABORT);
229 	if (bufp[0] == '\0')
230 		return (FALSE);
231 	if ((bp = dired_(bufp)) == NULL)
232 		return (FALSE);
233 
234 	curbp = bp;
235 	return (showbuffer(bp, curwp, WFFULL | WFMODE));
236 }
237 
238 /* ARGSUSED */
239 int
240 d_otherwindow(int f, int n)
241 {
242 	char		 dname[NFILEN], *bufp, *slash;
243 	struct buffer	*bp;
244 	struct mgwin	*wp;
245 
246 	if (curbp->b_fname && curbp->b_fname[0] != '\0') {
247 		(void)strlcpy(dname, curbp->b_fname, sizeof(dname));
248 		if ((slash = strrchr(dname, '/')) != NULL) {
249 			*(slash + 1) = '\0';
250 		}
251 	} else {
252 		if (getcwd(dname, sizeof(dname)) == NULL)
253 			dname[0] = '\0';
254 	}
255 
256 	if ((bufp = eread("Dired other window: ", dname, NFILEN,
257 	    EFDEF | EFNEW | EFCR)) == NULL)
258 		return (ABORT);
259 	else if (bufp[0] == '\0')
260 		return (FALSE);
261 	if ((bp = dired_(bufp)) == NULL)
262 		return (FALSE);
263 	if ((wp = popbuf(bp, WNONE)) == NULL)
264 		return (FALSE);
265 	curbp = bp;
266 	curwp = wp;
267 	return (TRUE);
268 }
269 
270 /* ARGSUSED */
271 int
272 d_del(int f, int n)
273 {
274 	if (n < 0)
275 		return (FALSE);
276 	while (n--) {
277 		if (llength(curwp->w_dotp) > 0)
278 			lputc(curwp->w_dotp, 0, 'D');
279 		if (lforw(curwp->w_dotp) != curbp->b_headp)
280 			curwp->w_dotp = lforw(curwp->w_dotp);
281 	}
282 	curwp->w_rflag |= WFEDIT | WFMOVE;
283 	curwp->w_doto = 0;
284 	return (TRUE);
285 }
286 
287 /* ARGSUSED */
288 int
289 d_undel(int f, int n)
290 {
291 	if (n < 0)
292 		return (d_undelbak(f, -n));
293 	while (n--) {
294 		if (llength(curwp->w_dotp) > 0)
295 			lputc(curwp->w_dotp, 0, ' ');
296 		if (lforw(curwp->w_dotp) != curbp->b_headp)
297 			curwp->w_dotp = lforw(curwp->w_dotp);
298 	}
299 	curwp->w_rflag |= WFEDIT | WFMOVE;
300 	curwp->w_doto = 0;
301 	return (TRUE);
302 }
303 
304 /* ARGSUSED */
305 int
306 d_undelbak(int f, int n)
307 {
308 	if (n < 0)
309 		return (d_undel(f, -n));
310 	while (n--) {
311 		if (llength(curwp->w_dotp) > 0)
312 			lputc(curwp->w_dotp, 0, ' ');
313 		if (lback(curwp->w_dotp) != curbp->b_headp)
314 			curwp->w_dotp = lback(curwp->w_dotp);
315 	}
316 	curwp->w_doto = 0;
317 	curwp->w_rflag |= WFEDIT | WFMOVE;
318 	return (TRUE);
319 }
320 
321 /* ARGSUSED */
322 int
323 d_findfile(int f, int n)
324 {
325 	struct buffer	*bp;
326 	int		 s;
327 	char		 fname[NFILEN];
328 
329 	if ((s = d_makename(curwp->w_dotp, fname, sizeof(fname))) == ABORT)
330 		return (FALSE);
331 	if (s == TRUE)
332 		bp = dired_(fname);
333 	else
334 		bp = findbuffer(fname);
335 	if (bp == NULL)
336 		return (FALSE);
337 	curbp = bp;
338 	if (showbuffer(bp, curwp, WFFULL) != TRUE)
339 		return (FALSE);
340 	if (bp->b_fname[0] != 0)
341 		return (TRUE);
342 	return (readin(fname));
343 }
344 
345 /* ARGSUSED */
346 int
347 d_ffotherwindow(int f, int n)
348 {
349 	char		 fname[NFILEN];
350 	int		 s;
351 	struct buffer	*bp;
352 	struct mgwin	*wp;
353 
354 	if ((s = d_makename(curwp->w_dotp, fname, sizeof(fname))) == ABORT)
355 		return (FALSE);
356 	if ((bp = (s ? dired_(fname) : findbuffer(fname))) == NULL)
357 		return (FALSE);
358 	if ((wp = popbuf(bp, WNONE)) == NULL)
359 		return (FALSE);
360 	curbp = bp;
361 	curwp = wp;
362 	if (bp->b_fname[0] != 0)
363 		return (TRUE);	/* never true for dired buffers */
364 	return (readin(fname));
365 }
366 
367 /* ARGSUSED */
368 int
369 d_expunge(int f, int n)
370 {
371 	struct line	*lp, *nlp;
372 	char		 fname[NFILEN], sname[NFILEN];
373 
374 	for (lp = bfirstlp(curbp); lp != curbp->b_headp; lp = nlp) {
375 		nlp = lforw(lp);
376 		if (llength(lp) && lgetc(lp, 0) == 'D') {
377 			switch (d_makename(lp, fname, sizeof(fname))) {
378 			case ABORT:
379 				ewprintf("Bad line in dired buffer");
380 				return (FALSE);
381 			case FALSE:
382 				if (unlink(fname) < 0) {
383 					(void)xbasename(sname, fname, NFILEN);
384 					ewprintf("Could not delete '%s'", sname);
385 					return (FALSE);
386 				}
387 				break;
388 			case TRUE:
389 				if (rmdir(fname) < 0) {
390 					(void)xbasename(sname, fname, NFILEN);
391 					ewprintf("Could not delete directory "
392 					    "'%s'", sname);
393 					return (FALSE);
394 				}
395 				break;
396 			}
397 			lfree(lp);
398 			curwp->w_bufp->b_lines--;
399 			curwp->w_rflag |= WFFULL;
400 		}
401 	}
402 	return (TRUE);
403 }
404 
405 /* ARGSUSED */
406 int
407 d_copy(int f, int n)
408 {
409 	char	frname[NFILEN], toname[NFILEN], sname[NFILEN], *bufp;
410 	int	ret;
411 	size_t	off;
412 	struct buffer *bp;
413 
414 	if (d_makename(curwp->w_dotp, frname, sizeof(frname)) != FALSE) {
415 		ewprintf("Not a file");
416 		return (FALSE);
417 	}
418 	off = strlcpy(toname, curbp->b_fname, sizeof(toname));
419 	if (off >= sizeof(toname) - 1) {	/* can't happen, really */
420 		ewprintf("Directory name too long");
421 		return (FALSE);
422 	}
423 	(void)xbasename(sname, frname, NFILEN);
424 	bufp = eread("Copy %s to: ", toname, sizeof(toname),
425 	    EFDEF | EFNEW | EFCR, sname);
426 	if (bufp == NULL)
427 		return (ABORT);
428 	else if (bufp[0] == '\0')
429 		return (FALSE);
430 	ret = (copy(frname, toname) >= 0) ? TRUE : FALSE;
431 	if (ret != TRUE)
432 		return (ret);
433 	bp = dired_(curbp->b_fname);
434 	return (showbuffer(bp, curwp, WFFULL | WFMODE));
435 }
436 
437 /* ARGSUSED */
438 int
439 d_rename(int f, int n)
440 {
441 	char		 frname[NFILEN], toname[NFILEN], *bufp;
442 	int		 ret;
443 	size_t		 off;
444 	struct buffer	*bp;
445 	char		 sname[NFILEN];
446 
447 	if (d_makename(curwp->w_dotp, frname, sizeof(frname)) != FALSE) {
448 		ewprintf("Not a file");
449 		return (FALSE);
450 	}
451 	off = strlcpy(toname, curbp->b_fname, sizeof(toname));
452 	if (off >= sizeof(toname) - 1) {	/* can't happen, really */
453 		ewprintf("Directory name too long");
454 		return (FALSE);
455 	}
456 	(void)xbasename(sname, frname, NFILEN);
457 	bufp = eread("Rename %s to: ", toname,
458 	    sizeof(toname), EFDEF | EFNEW | EFCR, sname);
459 	if (bufp == NULL)
460 		return (ABORT);
461 	else if (bufp[0] == '\0')
462 		return (FALSE);
463 	ret = (rename(frname, toname) >= 0) ? TRUE : FALSE;
464 	if (ret != TRUE)
465 		return (ret);
466 	bp = dired_(curbp->b_fname);
467 	return (showbuffer(bp, curwp, WFFULL | WFMODE));
468 }
469 
470 /* ARGSUSED */
471 void
472 reaper(int signo __attribute__((unused)))
473 {
474 	int	save_errno = errno, status;
475 
476 	while (waitpid(-1, &status, WNOHANG) >= 0)
477 		;
478 	errno = save_errno;
479 }
480 
481 /*
482  * Pipe the currently selected file through a shell command.
483  */
484 /* ARGSUSED */
485 int
486 d_shell_command(int f, int n)
487 {
488 	char		 command[512], fname[MAXPATHLEN], *bufp;
489 	struct buffer	*bp;
490 	struct mgwin	*wp;
491 	char		 sname[NFILEN];
492 
493 	bp = bfind("*Shell Command Output*", TRUE);
494 	if (bclear(bp) != TRUE)
495 		return (ABORT);
496 
497 	if (d_makename(curwp->w_dotp, fname, sizeof(fname)) != FALSE) {
498 		ewprintf("bad line");
499 		return (ABORT);
500 	}
501 
502 	command[0] = '\0';
503 	(void)xbasename(sname, fname, NFILEN);
504 	bufp = eread("! on %s: ", command, sizeof(command), EFNEW, sname);
505 	if (bufp == NULL)
506 		return (ABORT);
507 
508 	if (d_exec(0, bp, fname, "sh", "-c", command, NULL) != TRUE)
509 		return (ABORT);
510 
511 	if ((wp = popbuf(bp, WNONE)) == NULL)
512 		return (ABORT);	/* XXX - free the buffer?? */
513 	curwp = wp;
514 	curbp = wp->w_bufp;
515 	return (TRUE);
516 }
517 
518 /*
519  * Pipe input file to cmd and insert the command's output in the
520  * given buffer.  Each line will be prefixed with the given
521  * number of spaces.
522  */
523 static int
524 d_exec(int space, struct buffer *bp, const char *input, const char *cmd, ...)
525 {
526 	char	 buf[BUFSIZ];
527 	va_list	 ap;
528 	struct	 sigaction olda, newa;
529 	char	**argv = NULL, *cp;
530 	FILE	*fin;
531 	int	 fds[2] = { -1, -1 };
532 	int	 infd = -1;
533 	int	 ret = (ABORT), n;
534 	pid_t	 pid;
535 
536 	if (sigaction(SIGCHLD, NULL, &olda) == -1)
537 		return (ABORT);
538 
539 	/* Find the number of arguments. */
540 	va_start(ap, cmd);
541 	for (n = 2; va_arg(ap, char *) != NULL; n++)
542 		;
543 	va_end(ap);
544 
545 	/* Allocate and build the argv. */
546 	if ((argv = calloc(n, sizeof(*argv))) == NULL) {
547 		ewprintf("Can't allocate argv : %s", strerror(errno));
548 		goto out;
549 	}
550 
551 	n = 1;
552 	argv[0] = (char *)cmd;
553 	va_start(ap, cmd);
554 	while ((argv[n] = va_arg(ap, char *)) != NULL)
555 		n++;
556 	va_end(ap);
557 
558 	if (input == NULL)
559 		input = "/dev/null";
560 
561 	if ((infd = open(input, O_RDONLY)) == -1) {
562 		ewprintf("Can't open input file : %s", strerror(errno));
563 		goto out;
564 	}
565 
566 	if (pipe(fds) == -1) {
567 		ewprintf("Can't create pipe : %s", strerror(errno));
568 		goto out;
569 	}
570 
571 	newa.sa_handler = reaper;
572 	newa.sa_flags = 0;
573 	if (sigaction(SIGCHLD, &newa, NULL) == -1)
574 		goto out;
575 
576 	if ((pid = fork()) == -1) {
577 		ewprintf("Can't fork");
578 		goto out;
579 	}
580 
581 	switch (pid) {
582 	case 0: /* Child */
583 		close(fds[0]);
584 		dup2(infd, STDIN_FILENO);
585 		dup2(fds[1], STDOUT_FILENO);
586 		dup2(fds[1], STDERR_FILENO);
587 		if (execvp(argv[0], argv) == -1)
588 			ewprintf("Can't exec %s: %s", argv[0], strerror(errno));
589 		exit(1);
590 		break;
591 	default: /* Parent */
592 		close(infd);
593 		close(fds[1]);
594 		infd = fds[1] = -1;
595 		if ((fin = fdopen(fds[0], "r")) == NULL)
596 			goto out;
597 		while (fgets(buf, sizeof(buf), fin) != NULL) {
598 			cp = strrchr(buf, '\n');
599 			if (cp == NULL && !feof(fin)) {	/* too long a line */
600 				int c;
601 				addlinef(bp, "%*s%s...", space, "", buf);
602 				while ((c = getc(fin)) != EOF && c != '\n')
603 					;
604 				continue;
605 			} else if (cp)
606 				*cp = '\0';
607 			addlinef(bp, "%*s%s", space, "", buf);
608 		}
609 		fclose(fin);
610 		break;
611 	}
612 	ret = (TRUE);
613 
614 out:
615 	if (sigaction(SIGCHLD, &olda, NULL) == -1)
616 		ewprintf("Warning, couldn't reset previous signal handler");
617 	if (fds[0] != -1)
618 		close(fds[0]);
619 	if (fds[1] != -1)
620 		close(fds[1]);
621 	if (infd != -1)
622 		close(infd);
623 	if (argv != NULL)
624 		free(argv);
625 	return ret;
626 }
627 
628 /* ARGSUSED */
629 int
630 d_create_directory(int f, int n)
631 {
632 	char	 tocreate[MAXPATHLEN], *bufp;
633 	size_t  off;
634 	struct buffer	*bp;
635 
636 	off = strlcpy(tocreate, curbp->b_fname, sizeof(tocreate));
637 	if (off >= sizeof(tocreate) - 1)
638 		return (FALSE);
639 	if ((bufp = eread("Create directory: ", tocreate,
640 	    sizeof(tocreate), EFDEF | EFNEW | EFCR)) == NULL)
641 		return (ABORT);
642 	else if (bufp[0] == '\0')
643 		return (FALSE);
644 	if (mkdir(tocreate, 0755) == -1) {
645 		ewprintf("Creating directory: %s, %s", strerror(errno),
646 		    tocreate);
647 		return (FALSE);
648 	}
649 	bp = dired_(curbp->b_fname);
650 	return (showbuffer(bp, curwp, WFFULL | WFMODE));
651 }
652 
653 static int
654 d_makename(struct line *lp, char *fn, size_t len)
655 {
656 	int	 start, nlen;
657 	char	*namep;
658 
659 	if (d_warpdot(lp, &start) == FALSE)
660 		return (ABORT);
661 	namep = &lp->l_text[start];
662 	nlen = llength(lp) - start;
663 
664 	if (snprintf(fn, len, "%s%.*s", curbp->b_fname, nlen, namep) >= len)
665 		return (ABORT); /* Name is too long. */
666 
667 	/* Return TRUE if the entry is a directory. */
668 	return ((lgetc(lp, 2) == 'd') ? TRUE : FALSE);
669 }
670 
671 #define NAME_FIELD	9
672 
673 static int
674 d_warpdot(struct line *dotp, int *doto)
675 {
676 	char *tp = dotp->l_text;
677 	int off = 0, field = 0, len;
678 
679 	/*
680 	 * Find the byte offset to the (space-delimited) filename
681 	 * field in formatted ls output.
682 	 */
683 	len = llength(dotp);
684 	while (off < len) {
685 		if (tp[off++] == ' ') {
686 			if (++field == NAME_FIELD) {
687 				*doto = off;
688 				return (TRUE);
689 			}
690 			/* Skip the space. */
691 			while (off < len && tp[off] == ' ')
692 				off++;
693 		}
694 	}
695 	/* We didn't find the field. */
696 	*doto = 0;
697 	return (FALSE);
698 }
699 
700 static int
701 d_forwpage(int f, int n)
702 {
703 	forwpage(f | FFRAND, n);
704 	return (d_warpdot(curwp->w_dotp, &curwp->w_doto));
705 }
706 
707 static int
708 d_backpage (int f, int n)
709 {
710 	backpage(f | FFRAND, n);
711 	return (d_warpdot(curwp->w_dotp, &curwp->w_doto));
712 }
713 
714 static int
715 d_forwline (int f, int n)
716 {
717 	forwline(f | FFRAND, n);
718 	return (d_warpdot(curwp->w_dotp, &curwp->w_doto));
719 }
720 
721 static int
722 d_backline (int f, int n)
723 {
724 	backline(f | FFRAND, n);
725 	return (d_warpdot(curwp->w_dotp, &curwp->w_doto));
726 }
727 
728 /*
729  * XXX dname needs to have enough place to store an additional '/'.
730  */
731 struct buffer *
732 dired_(char *dname)
733 {
734 	struct buffer	*bp;
735 	int		 len, i;
736 
737 	if ((fopen(dname,"r")) == NULL) {
738 		if (errno == EACCES)
739 			ewprintf("Permission denied");
740 		return (NULL);
741 	}
742 	if ((dname = adjustname(dname, FALSE)) == NULL) {
743 		ewprintf("Bad directory name");
744 		return (NULL);
745 	}
746 	/* this should not be done, instead adjustname() should get a flag */
747 	len = strlen(dname);
748 	if (dname[len - 1] != '/') {
749 		dname[len++] = '/';
750 		dname[len] = '\0';
751 	}
752 	if ((bp = findbuffer(dname)) == NULL) {
753 		ewprintf("Could not create buffer");
754 		return (NULL);
755 	}
756 	if (bclear(bp) != TRUE)
757 		return (NULL);
758 	bp->b_flag |= BFREADONLY;
759 
760 	if ((d_exec(2, bp, NULL, "ls", "-al", dname, NULL)) != TRUE)
761 		return (NULL);
762 
763 	/* Find the line with ".." on it. */
764 	bp->b_dotp = bfirstlp(bp);
765 	for (i = 0; i < bp->b_lines; i++) {
766 		bp->b_dotp = lforw(bp->b_dotp);
767 		if (d_warpdot(bp->b_dotp, &bp->b_doto) == FALSE)
768 			continue;
769 		if (strcmp(ltext(bp->b_dotp) + bp->b_doto, "..") == 0)
770 			break;
771 	}
772 
773 	/* We want dot on the entry right after "..", if possible. */
774 	if (++i < bp->b_lines - 2)
775 		bp->b_dotp = lforw(bp->b_dotp);
776 	d_warpdot(bp->b_dotp, &bp->b_doto);
777 
778 	(void)strlcpy(bp->b_fname, dname, sizeof(bp->b_fname));
779 	(void)strlcpy(bp->b_cwd, dname, sizeof(bp->b_cwd));
780 	if ((bp->b_modes[1] = name_mode("dired")) == NULL) {
781 		bp->b_modes[0] = name_mode("fundamental");
782 		ewprintf("Could not find mode dired");
783 		return (NULL);
784 	}
785 	bp->b_nmodes = 1;
786 	return (bp);
787 }
788