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