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