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