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