2 * Copyright Neil Brown ©2015-2023 <neil@brown.name>
3 * May be distributed under terms of GPLv2 - see file:COPYING
5 * directory listing as a document.
7 * The 'text' of the document is a single '\n' char per director entry:
9 * Each char has a set of attributes which give details
32 #define _GNU_SOURCE /* for asprintf */
38 #include <sys/fcntl.h>
45 #define PRIVATE_DOC_REF
51 #define DOC_DATA_TYPE struct directory
52 #define DOC_NEXT(d,m,r,b) dir_next(d,r,b)
53 #define DOC_PREV(d,m,r,b) dir_prev(d,r,b)
55 #define PANE_DATA_VOID
62 struct attrset *attrs;
69 struct list_head ents;
74 #include "core-pane.h"
76 static void get_stat(struct directory *dr safe, struct dir_ent *de safe);
78 static char *key(struct list_head *le, const void *data)
82 return list_entry(le, struct dir_ent, lst)->name;
85 static bool add_ent(struct list_head *lst safe, struct dirent *de safe)
92 dre = malloc(sizeof(*dre));
93 dre->name = strdup(de->d_name);
96 if (strcmp(de->d_name, ".") == 0)
98 else if (strcmp(de->d_name, "..") == 0)
100 else switch (de->d_type) {
101 case DT_BLK: dre->ch = 'b'; break;
102 case DT_CHR: dre->ch = 'c'; break;
103 case DT_DIR: dre->ch = 'd'; break;
104 case DT_FIFO:dre->ch = 'p'; break;
105 case DT_LNK: dre->ch = 'l'; break;
106 case DT_REG: dre->ch = 'f'; break;
107 case DT_SOCK:dre->ch = 's'; break;
109 case DT_UNKNOWN:dre->ch = '?'; break;
111 list_add(&dre->lst, lst);
115 static void load_dir(struct list_head *lst safe, int fd)
120 dir = fdopendir(dup(fd));
123 while ((res = readdir(dir)) != NULL)
125 sort_list(lst, key, NULL);
129 static struct map *dir_map;
130 DEF_LOOKUP_CMD(dir_handle, dir_map);
134 struct directory *dr;
137 p = doc_register(ci->home, &dir_handle.c);
141 INIT_LIST_HEAD(&dr->ents);
144 return comm_call(ci->comm2, "callback:doc", p);
149 if (ci->num2 != S_IFDIR)
151 return dir_new_func(ci);
154 DEF_CMD(dir_load_file)
157 const char *name = ci->str;
158 struct directory *dr = ci->home->doc_data;
159 struct list_head new;
160 struct dir_ent *de1, *de2;
161 struct mark *prev, *m;
165 m = mark_new(ci->home);
171 fd = open(dr->fname, O_RDONLY|O_DIRECTORY);
177 INIT_LIST_HEAD(&new);
179 de2 = list_first_entry_or_null(&new, struct dir_ent, lst);
180 while (m->ref.d || de2) {
183 (de2 == NULL || strcmp(de1->name, de2->name) < 0)) {
184 /* de1 doesn't exist in new: need to delete it. */
186 struct dir_ent *de = de1;
187 if (de1 == list_last_entry(&dr->ents,
188 struct dir_ent, lst))
191 de1 = list_next_entry(de1, lst);
192 for (m2 = m; m2 && m2->ref.d == de;
195 attr_free(&de->attrs);
201 doc_prev(ci->home, prev);
204 (de1 == NULL || strcmp(de2->name, de1->name) < 0)) {
205 /* de2 doesn't already exist, so add it before de1 */
208 list_add_tail(&de2->lst, &de1->lst);
210 list_add_tail(&de2->lst, &dr->ents);
213 doc_prev(ci->home, prev);
215 } else if (de1 && de2) {
216 /* de1 and de2 are the same. Just step over de1 and
219 bool changed = False;
220 if (de1->st.st_mode) {
221 /* Need to check if stat info has changed */
223 if (de1->st.st_mode != de2->st.st_mode ||
224 de1->st.st_size != de2->st.st_size ||
225 de1->st.st_mtime != de2->st.st_mtime ||
226 de1->st.st_ctime != de2->st.st_ctime) {
235 pane_notify("doc:replaced", ci->home,
241 doc_next(ci->home, m);
242 mark_step_sharesref(m,0);
245 attr_free(&de2->attrs);
249 de2 = list_first_entry_or_null(&new, struct dir_ent, lst);
252 pane_notify("doc:replaced", ci->home, 0, prev, NULL,
259 int l = strlen(name);
261 fstat(fd, &dr->stat);
262 dr->fname = malloc(l+2);
263 strcpy(dr->fname, name);
264 if (l > 1 && dr->fname[l-1] == '/')
265 dr->fname[l-1] = '\0';
266 dname = strrchr(dr->fname, '/');
267 if (dname && dname[1])
271 call("doc:set-name", ci->home, 0, NULL, dname);
273 strcat(dr->fname, "/");
280 DEF_CMD(dir_revisited)
282 struct directory *dr = ci->home->doc_data;
286 /* Being buried, not visited */
289 if (stat(dr->fname, &st) == 0 &&
290 (st.st_ino != dr->stat.st_ino ||
291 st.st_dev != dr->stat.st_dev ||
292 st.st_mtime != dr->stat.st_mtime ||
293 st.st_mtim.tv_nsec != dr->stat.st_mtim.tv_nsec)) {
295 call("doc:load-file", ci->home, 2, NULL, NULL, -1);
296 asprintf(&msg, "Directory %s reloaded", dr->fname);
297 call("Message", ci->focus, 0, NULL, msg);
303 DEF_CMD(dir_same_file)
307 struct directory *dr = ci->home->doc_data;
311 if (fstat(fd, &stb) != 0)
313 if (! (dr->stat.st_ino == stb.st_ino &&
314 dr->stat.st_dev == stb.st_dev))
316 /* Let's reload it now */
317 home_call(ci->home, "doc:load-file", ci->focus, 0, NULL, NULL, fd);
321 static inline wint_t dir_next(struct pane *p safe, struct doc_ref *r safe, bool bytes)
323 struct directory *dr = p->doc_data;
324 struct dir_ent *d = r->d;
329 if (d == list_last_entry(&dr->ents,
330 struct dir_ent, lst))
333 d = list_next_entry(d, lst);
339 static inline wint_t dir_prev(struct pane *p safe, struct doc_ref *r safe, bool bytes)
341 struct directory *dr = p->doc_data;
342 struct dir_ent *d = r->d;
344 if (d == list_first_entry(&dr->ents, struct dir_ent, lst))
347 d = list_last_entry(&dr->ents, struct dir_ent, lst);
349 d = list_prev_entry(d, lst);
359 return do_char_byte(ci);
364 struct directory *dr = ci->home->doc_data;
365 struct mark *m = ci->mark;
370 mark_to_end(ci->home, m, ci->num != 1);
371 if (list_empty(&dr->ents) || ci->num != 1)
374 m->ref.d = list_first_entry(&dr->ents, struct dir_ent, lst);
379 static void get_stat(struct directory *dr safe, struct dir_ent *de safe)
385 dfd = open(dr->fname, O_RDONLY);
388 if (fstatat(dfd, de->name, &de->st, AT_SYMLINK_NOFOLLOW) != 0) {
389 de->st.st_mode = 0xffff;
391 } else if ((de->st.st_mode & S_IFMT) == S_IFLNK &&
392 fstatat(dfd, de->name, &st, 0) == 0 &&
393 (st.st_mode & S_IFMT) == S_IFDIR)
398 static char *fmt_num(struct dir_ent *de safe, long num)
400 sprintf(de->nbuf, "%ld", num);
404 static char *save_str(struct dir_ent *de safe, char *str safe)
406 strncpy(de->nbuf, str, sizeof(de->nbuf));
407 de->nbuf[sizeof(de->nbuf)-1] = 0;
411 static char *fmt_date(struct dir_ent *de safe, time_t t, struct pane *p safe)
414 time_t now = time(NULL);
416 if (edlib_testing(p)) {
420 localtime_r(&t, &tm);
421 if (t > now || t < now - 10*30*24*3600)
422 strftime(de->nbuf, sizeof(de->nbuf),
425 strftime(de->nbuf, sizeof(de->nbuf),
430 static char *fmt_size(struct dir_ent *de safe, loff_t size)
433 snprintf(de->nbuf, sizeof(de->nbuf),
435 else if (size < 1024*10)
436 snprintf(de->nbuf, sizeof(de->nbuf),
437 "%ld.%02ldK", size/1024, (size%1023)*100 / 1024);
438 else if (size < 1024*1024)
439 snprintf(de->nbuf, sizeof(de->nbuf),
441 else if (size < 1024L*1024*10)
442 snprintf(de->nbuf, sizeof(de->nbuf),
443 "%ld.%02ldM", size/1024/1024, ((size/1024)%1023)*100 / 1024);
444 else if (size < 1024L*1024*1024)
445 snprintf(de->nbuf, sizeof(de->nbuf),
446 "%ldM", size/1024/1024);
447 else if (size < 1024L*1024*1024*10)
448 snprintf(de->nbuf, sizeof(de->nbuf),
449 "%ld.%02ldG", size/1024/1024/1024, ((size/1024/1024)%1023)*100 / 1024);
451 snprintf(de->nbuf, sizeof(de->nbuf),
452 "%ldG", size/1024/1024/1024);
456 static char *pwname(int uid)
458 static int last_uid = -1;
459 static char *last_name = NULL;
462 if (uid != last_uid) {
465 if (pw && pw->pw_name)
466 last_name = strdup(pw->pw_name);
474 static char *grname(int gid)
476 static int last_gid = -1;
477 static char *last_name = NULL;
480 if (gid != last_gid) {
483 if (gr && gr->gr_name)
484 last_name = strdup(gr->gr_name);
492 static const char *_dir_get_attr(struct pane *home safe, struct mark *m safe,
493 const char *attr safe)
496 struct directory *dr = home->doc_data;
502 if (strcmp(attr, "name") == 0) {
504 } else if (strcmp(attr, "type") == 0) {
505 de->nbuf[0] = de->ch;
508 } else if (strcmp(attr, "size") == 0) {
510 return fmt_num(de, de->st.st_size);
511 } else if (strcmp(attr, "hsize") == 0) {
513 if (strchr(".:d", de->ch) &&
515 /* Size might not be reliable for testing */
517 return fmt_size(de, de->st.st_size);
518 } else if (strcmp(attr, "mtime") == 0) {
520 return fmt_num(de, de->st.st_mtime);
521 } else if (strcmp(attr, "mdate") == 0) {
523 return fmt_date(de, de->st.st_mtime, home);
524 } else if (strcmp(attr, "atime") == 0) {
526 return fmt_num(de, de->st.st_atime);
527 } else if (strcmp(attr, "adate") == 0) {
529 return fmt_date(de, de->st.st_atime, home);
530 } else if (strcmp(attr, "ctime") == 0) {
532 return fmt_num(de, de->st.st_ctime);
533 } else if (strcmp(attr, "cdate") == 0) {
535 return fmt_date(de, de->st.st_ctime, home);
536 } else if (strcmp(attr, "uid") == 0) {
538 return fmt_num(de, de->st.st_uid);
539 } else if (strcmp(attr, "gid") == 0) {
541 return fmt_num(de, de->st.st_gid);
542 } else if (strcmp(attr, "user") == 0) {
545 if (edlib_testing(home))
547 n = pwname(de->st.st_uid);
549 return save_str(de, n);
551 return fmt_num(de, de->st.st_uid);
552 } else if (strcmp(attr, "group") == 0) {
555 if (edlib_testing(home))
557 n = grname(de->st.st_gid);
559 return save_str(de, n);
561 return fmt_num(de, de->st.st_gid);
562 } else if (strcmp(attr, "mode") == 0) {
564 return fmt_num(de, de->st.st_mode & 0777);
565 } else if (strcmp(attr, "perms") == 0) {
571 mode = de->st.st_mode;
572 switch (mode & S_IFMT) {
573 case S_IFREG: *c ++ = '-'; break;
574 case S_IFDIR: *c ++ = 'd'; break;
575 case S_IFBLK: *c ++ = 'b'; break;
576 case S_IFCHR: *c ++ = 'c'; break;
577 case S_IFSOCK:*c ++ = 's'; break;
578 case S_IFLNK: *c ++ = 'l'; break;
579 default: *c ++ = '?'; break;
581 if (edlib_testing(home) && de->ch == ':')
582 /* ".." might not be under control of the test */
584 for (i = 0; i < 3; i++) {
585 *c ++ = (mode & 0400) ? 'r':'-';
586 *c ++ = (mode & 0200) ? 'w':'-';
587 *c ++ = (mode & 0100) ? 'x':'-';
592 } else if (strcmp(attr, "suffix") == 0) {
595 if (strchr(".:dL", de->ch))
598 } else if (strcmp(attr, "arrow") == 0) {
599 if (strchr("lL", de->ch))
603 } else if (strcmp(attr, "target") == 0) {
608 if (strchr("lL", de->ch) == NULL)
610 dfd = open(dr->fname, O_RDONLY);
613 len = readlinkat(dfd, de->name, buf, sizeof(buf));
615 if (len <= 0 || len >= (int)sizeof(buf))
618 return strsave(home, buf);
620 return attr_find(de->attrs, attr);
623 DEF_CMD(dir_doc_get_attr)
625 struct mark *m = ci->mark;
626 const char *attr = ci->str;
631 val = _dir_get_attr(ci->home, m, attr);
635 comm_call(ci->comm2, "callback:get_attr", ci->focus, 0, m, val,
640 DEF_CMD(dir_doc_set_attr)
642 struct mark *m = ci->mark;
643 const char *attr = ci->str;
644 const char *val = ci->str2;
650 attr_set_str(&m->ref.d->attrs, attr, val);
651 pane_notify("doc:replaced-attr", ci->home, 1, ci->mark);
655 DEF_CMD(dir_get_attr)
657 struct directory *dr = ci->home->doc_data;
658 const char *attr = ci->str;
664 if ((val = attr_find(ci->home->attrs, attr)) != NULL)
666 else if (strcmp(attr, "heading") == 0)
668 else if (strcmp(attr, "render-default") == 0)
670 else if (strcmp(attr, "render-simple") == 0)
672 else if (strcmp(attr, "view-default") == 0)
674 else if (strcmp(attr, "doc-type") == 0)
676 else if (strcmp(attr, "line-format") == 0)
678 else if (strcmp(attr, "filename") == 0)
682 comm_call(ci->comm2, "callback:get_attr", ci->focus, 0, NULL, val);
686 DEF_CMD(dir_val_marks)
688 struct directory *dr = ci->home->doc_data;
692 if (!ci->mark || !ci->mark2)
695 if (ci->mark->ref.d == ci->mark2->ref.d) {
696 if (ci->mark->ref.ignore < ci->mark2->ref.ignore)
698 LOG("dir_val_marks: same buf, bad offset: %u, %u",
699 ci->mark->ref.ignore, ci->mark2->ref.ignore);
702 if (ci->mark->ref.d == NULL) {
703 LOG("dir_val_marks: mark.d is NULL");
707 list_for_each_entry(de, &dr->ents, lst) {
708 if (ci->mark->ref.d == de)
710 if (ci->mark2->ref.d == de) {
713 LOG("dir_val_marks: mark2.d found before mark1");
717 if (ci->mark2->ref.d == NULL) {
720 LOG("dir_val_marks: mark2.d (NULL) found before mark1");
724 LOG("dir_val_marks: Neither mark found in de list");
726 LOG("dir_val_marks: mark2 not found in de list");
730 DEF_CMD_CLOSED(dir_destroy)
732 struct directory *dr = ci->home->doc_data;
734 while (!list_empty(&dr->ents)) {
735 struct dir_ent *de = list_entry(dr->ents.next,
736 struct dir_ent, lst);
738 attr_free(&de->attrs);
746 DEF_CMD(dir_shares_ref)
751 DEF_CMD(dir_debug_mark)
754 struct mark *m = ci->mark;
757 if (!m || m->owner != ci->home || !ci->comm2)
761 ret = strdup("M:FREED");
763 ret = strdup("M:EOF");
765 asprintf(&ret, "M:%s(#%x)", de->name, m->ref.ignore);
766 comm_call(ci->comm2, "cb", ci->focus, 0, NULL, ret);
771 static struct map *dirview_map;
772 DEF_LOOKUP_CMD(dirview_handle, dirview_map);
774 static int dir_open(struct pane *focus safe,
775 struct mark *m, bool other, bool follow)
777 /* close this pane, open the given file. */
779 struct pane *par, *p;
781 char *dirname, *basename, *type;
785 m = call_ret(mark, "doc:point", focus);
788 dirname = pane_attr_get(focus, "filename");
789 basename = pane_mark_attr(focus, m, "name");
790 type = pane_mark_attr(focus, m, "type");
791 if (!dirname || !basename || !type)
794 asprintf(&fname, "%s/%s", dirname, basename);
798 if (follow && (type[0] == 'l' || type[0] == 'L')) {
799 /* Fname is a symlink. Read it and open
800 * that directly. Only follow this step once.
805 ret = readlink(fname, path, sizeof(path));
806 if (ret > 0 && ret < (int)sizeof(path)) {
809 asprintf(&fname, "%s", path);
811 asprintf(&fname, "%s/%s", dirname, path);
816 fd = open(fname, O_RDONLY);
818 p = call_ret(pane, "doc:open", focus, fd, NULL, fname);
821 p = call_ret(pane, "doc:from-text", focus, 0, NULL, fname,
822 0, NULL, "File not found\n");
827 par = home_call_ret(pane, focus, "DocPane", p);
829 pane_take_focus(par);
832 par = call_ret(pane, "OtherPane", focus);
834 par = call_ret(pane, "ThisPane", focus);
836 p = home_call_ret(pane, p, "doc:attach-view", par);
842 static int dir_open_alt(struct pane *focus safe,
843 struct mark *m, char cmd)
845 /* close this pane, open the given file. */
848 char *dirname, *basename;
854 dirname = pane_attr_get(focus, "filename");
855 basename = pane_mark_attr(focus, m, "name");
856 if (!dirname || !basename)
859 asprintf(&fname, "%s/%s", dirname, basename);
862 fd = open(fname, O_RDONLY);
865 struct pane *new = call_ret(pane, "doc:open", focus,
870 snprintf(buf, sizeof(buf), "cmd-%c", cmd);
871 p = call_ret(pane, "ThisPane", focus);
875 p = home_call_ret(pane, new, "doc:attach-view", p,
878 struct pane *doc = call_ret(pane, "doc:from-text", focus,
880 0, NULL, "File not found\n");
883 p = call_ret(pane, "ThisPane", focus);
886 p = home_call_ret(pane, doc, "doc:attach-view", p, 1);
895 return dir_open(ci->focus, ci->mark, False, ci->num == 1);
898 DEF_CMD(dir_do_open_other)
900 return dir_open(ci->focus, ci->mark, True, ci->num == 1);
903 DEF_CMD(dir_do_reload)
905 return call("doc:load-file", ci->focus, 0, NULL, NULL, -1);
908 DEF_CMD(dir_do_mark_del)
910 call("doc:set-attr", ci->focus, 0, ci->mark, "dir-cmd",
912 call("doc:EOL", ci->focus, 1, ci->mark, NULL, 1);
918 call("doc:set-attr", ci->focus, 0, ci->mark, "dir-cmd",
920 call("doc:EOL", ci->focus, 1, ci->mark, NULL, 1);
926 call("doc:set-attr", ci->focus, 0, ci->mark, "dir-cmd",
928 call("doc:EOL", ci->focus, 1, ci->mark, NULL, 1);
932 DEF_CMD(dir_un_mark_back)
934 call("doc:EOL", ci->focus, -2, ci->mark);
935 call("doc:set-attr", ci->focus, 0, ci->mark, "dir-cmd",
942 return call("doc:destroy", ci->home);
945 DEF_CMD(dir_do_special)
947 const char *c = ksuffix(ci, "doc:cmd-");
949 return dir_open_alt(ci->focus, ci->mark, c[0]);
952 static void add_name(struct buf *b safe, char *name safe)
954 if (strchr(name, '\'') == NULL) {
963 if (strchr("\"$`\\", *name))
965 buf_append_byte(b, *name);
972 static char *collect_names(struct pane *p safe, char *type,
975 struct mark *m = mark_new(p);
981 while (type && doc_following(p, m) != WEOF) {
983 t = pane_mark_attr(p, m, "dir-cmd");
984 if (!t || strcmp(t, type) != 0) {
988 name = pane_mark_attr(p, m, "name");
989 call("doc:set-attr", p, 0, m, "dir-cmd");
996 if (!b.len && mark) {
997 char *name = pane_mark_attr(p, mark, "name");
1001 return buf_final(&b);
1004 DEF_CMD(dir_expunge)
1009 names = collect_names(ci->focus, "D", NULL);
1011 if (!names || !names[0]) {
1013 call("Message:modal", ci->focus, 0, NULL,
1014 "No files marked for deletion");
1017 cmd = strconcat(ci->focus, "rm -f ", names);
1018 p = call_ret(pane, "attach-shell-prompt", ci->focus, 0, NULL, cmd);
1020 // put cursor after the "-f"
1021 call("doc:file", p, -1);
1022 call("doc:char", p, 5);
1023 pane_add_notify(ci->home, p, "Notify:Close");
1029 DEF_CMD(dir_chmodown)
1035 if (strcmp(ci->key, "K:A-o") == 0)
1040 names = collect_names(ci->focus, "*", ci->mark);
1041 if (!names || !*names) {
1043 call("Message:modal", ci->focus, 0, NULL,
1044 strconcat(ci->focus, "No file for ", which));
1047 cmd = strconcat(ci->focus, which, " ", names);
1048 p = call_ret(pane, "attach-shell-prompt", ci->focus, 0, NULL, cmd);
1050 call("doc:file", p, -1);
1051 call("doc:char", p, strlen(which) + 1);
1052 pane_add_notify(ci->home, p, "Notify:Close");
1062 char *dirname = pane_attr_get(ci->focus, "dirname");
1068 names = collect_names(ci->focus, "*", NULL);
1069 if (names && *names) {
1070 cmd = strconcat(ci->focus, "mv --target-directory ",
1072 prefix = strlen(cmd) - strlen(names);
1075 names = collect_names(ci->focus, NULL, ci->mark);
1076 if (!names || !*names) {
1078 call("Message:modal", ci->focus, 0, NULL,
1079 "No file for rename");
1082 cmd = strconcat(ci->focus, "mv", names, " ", dirname);
1083 prefix = strlen(cmd);
1085 p = call_ret(pane, "attach-shell-prompt", ci->focus, 0, NULL, cmd);
1087 call("doc:file", p, -1);
1088 call("doc:char", p, prefix);
1089 pane_add_notify(ci->home, p, "Notify:Close");
1095 DEF_CMD(dirview_attach)
1099 p = call_ret(pane, "attach-viewer", ci->focus);
1102 p = pane_register(p, 0, &dirview_handle.c);
1105 attr_set_str(&p->attrs, "line-format",
1106 "<fg:green-40>%flag</> <fg:red>%perms</> %mdate:13 %user:10 %group:10%hsize:-6 <fg:blue>%name%suffix</>%arrow<fg:green-30>%target</>");
1107 attr_set_str(&p->attrs, "heading",
1108 "<bold,fg:blue,underline> Perms Mtime Owner Group Size File Name</>");
1110 comm_call(ci->comm2, "cb", p);
1114 DEF_CMD(dirview_clone)
1118 p = pane_register(ci->focus, 0, &dirview_handle.c);
1122 pane_clone_children(ci->home, p);
1126 DEF_CMD(dirview_doc_get_attr)
1128 struct mark *m = ci->mark;
1129 const char *attr = ci->str;
1134 if (strcmp(attr, "flag") != 0)
1135 return Efallthrough;
1136 val = pane_mark_attr(ci->home->parent, m, "dir-cmd");
1139 comm_call(ci->comm2, "cb", ci->focus, 0, m, val, 0, NULL, attr);
1143 DEF_CMD(dirview_close_notify)
1145 /* shell window closed, maybe something was changed - check */
1146 if (ci->key[0] == 'c') {
1148 call("doc:notify:doc:revisit", ci->focus, 1);
1151 call_comm("event:timer", ci->home, &dirview_close_notify, 500);
1155 void edlib_init(struct pane *ed safe)
1157 call_comm("global-set-command", ed, &dir_new, 0, NULL, "attach-doc-dir");
1158 call_comm("global-set-command", ed, &dir_new2, 0, NULL, "open-doc-dir");
1160 dir_map = key_alloc();
1161 key_add_chain(dir_map, doc_default_cmd);
1163 key_add(dir_map, "doc:load-file", &dir_load_file);
1164 key_add(dir_map, "doc:same-file", &dir_same_file);
1165 key_add(dir_map, "doc:set-ref", &dir_set_ref);
1166 key_add(dir_map, "doc:get-attr", &dir_doc_get_attr);
1167 key_add(dir_map, "doc:set-attr", &dir_doc_set_attr);
1168 key_add(dir_map, "doc:char", &dir_char);
1169 key_add(dir_map, "doc:notify:doc:revisit", &dir_revisited);
1170 key_add(dir_map, "doc:debug:mark", &dir_debug_mark);
1172 key_add(dir_map, "doc:shares-ref", &dir_shares_ref);
1174 key_add(dir_map, "get-attr", &dir_get_attr);
1175 key_add(dir_map, "Close", &dir_destroy);
1176 if(0)key_add(dir_map, "debug:validate-marks", &dir_val_marks);
1178 call_comm("global-set-command", ed, &dirview_attach, 0, NULL,
1181 dirview_map = key_alloc();
1182 key_add(dirview_map, "doc:cmd-f", &dir_do_open);
1183 key_add(dirview_map, "doc:cmd-o", &dir_do_open_other);
1184 key_add(dirview_map, "doc:cmd-\n", &dir_do_open);
1185 key_add(dirview_map, "doc:cmd:Enter", &dir_do_open);
1186 key_add(dirview_map, "doc:cmd-g", &dir_do_reload);
1187 key_add(dirview_map, "doc:cmd-q", &dir_do_quit);
1188 key_add(dirview_map, "doc:cmd-d", &dir_do_mark_del);
1189 key_add(dirview_map, "doc:cmd-m", &dir_do_mark);
1190 key_add(dirview_map, "doc:cmd-u", &dir_un_mark);
1191 key_add(dirview_map, "doc:cmd-x", &dir_expunge);
1192 key_add(dirview_map, "doc:cmd-r", &dir_rename);
1193 key_add(dirview_map, "K:A-o", &dir_chmodown);
1194 key_add(dirview_map, "K:A-m", &dir_chmodown);
1195 key_add(dirview_map, "K:Del", &dir_un_mark_back);
1196 key_add_range(dirview_map, "doc:cmd-A", "doc:cmd-Z", &dir_do_special);
1197 key_add(dirview_map, "doc:get-attr", &dirview_doc_get_attr);
1198 key_add(dirview_map, "Clone", &dirview_clone);
1199 key_add(dirview_map, "Notify:Close", &dirview_close_notify);