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)
60 struct attrset *attrs;
67 struct list_head ents;
72 #include "core-pane.h"
74 static void get_stat(struct directory *dr safe, struct dir_ent *de safe);
76 static char *key(struct list_head *le, const void *data)
80 return list_entry(le, struct dir_ent, lst)->name;
83 static bool add_ent(struct list_head *lst safe, struct dirent *de safe)
90 dre = malloc(sizeof(*dre));
91 dre->name = strdup(de->d_name);
94 if (strcmp(de->d_name, ".") == 0)
96 else if (strcmp(de->d_name, "..") == 0)
98 else switch (de->d_type) {
99 case DT_BLK: dre->ch = 'b'; break;
100 case DT_CHR: dre->ch = 'c'; break;
101 case DT_DIR: dre->ch = 'd'; break;
102 case DT_FIFO:dre->ch = 'p'; break;
103 case DT_LNK: dre->ch = 'l'; break;
104 case DT_REG: dre->ch = 'f'; break;
105 case DT_SOCK:dre->ch = 's'; break;
107 case DT_UNKNOWN:dre->ch = '?'; break;
109 list_add(&dre->lst, lst);
113 static void load_dir(struct list_head *lst safe, int fd)
118 dir = fdopendir(dup(fd));
121 while ((res = readdir(dir)) != NULL)
123 sort_list(lst, key, NULL);
127 static struct map *dir_map;
128 DEF_LOOKUP_CMD(dir_handle, dir_map);
132 struct directory *dr;
135 p = doc_register(ci->home, &dir_handle.c);
139 INIT_LIST_HEAD(&dr->ents);
142 return comm_call(ci->comm2, "callback:doc", p);
147 if (ci->num2 != S_IFDIR)
149 return dir_new_func(ci);
152 DEF_CMD(dir_load_file)
155 const char *name = ci->str;
156 struct directory *dr = ci->home->doc_data;
157 struct list_head new;
158 struct dir_ent *de1, *de2;
159 struct mark *prev, *m;
163 m = mark_new(ci->home);
169 fd = open(dr->fname, O_RDONLY|O_DIRECTORY);
175 INIT_LIST_HEAD(&new);
177 de2 = list_first_entry_or_null(&new, struct dir_ent, lst);
178 while (m->ref.d || de2) {
181 (de2 == NULL || strcmp(de1->name, de2->name) < 0)) {
182 /* de1 doesn't exist in new: need to delete it. */
184 struct dir_ent *de = de1;
185 if (de1 == list_last_entry(&dr->ents,
186 struct dir_ent, lst))
189 de1 = list_next_entry(de1, lst);
190 for (m2 = m; m2 && m2->ref.d == de;
193 attr_free(&de->attrs);
199 doc_prev(ci->home, prev);
202 (de1 == NULL || strcmp(de2->name, de1->name) < 0)) {
203 /* de2 doesn't already exist, so add it before de1 */
206 list_add_tail(&de2->lst, &de1->lst);
208 list_add_tail(&de2->lst, &dr->ents);
211 doc_prev(ci->home, prev);
213 } else if (de1 && de2) {
214 /* de1 and de2 are the same. Just step over de1 and
217 bool changed = False;
218 if (de1->st.st_mode) {
219 /* Need to check if stat info has changed */
221 if (de1->st.st_mode != de2->st.st_mode ||
222 de1->st.st_size != de2->st.st_size ||
223 de1->st.st_mtime != de2->st.st_mtime ||
224 de1->st.st_ctime != de2->st.st_ctime) {
233 pane_notify("doc:replaced", ci->home,
239 doc_next(ci->home, m);
240 mark_step_sharesref(m,0);
243 attr_free(&de2->attrs);
247 de2 = list_first_entry_or_null(&new, struct dir_ent, lst);
250 pane_notify("doc:replaced", ci->home, 0, prev, NULL,
257 int l = strlen(name);
259 fstat(fd, &dr->stat);
260 dr->fname = malloc(l+2);
261 strcpy(dr->fname, name);
262 if (l > 1 && dr->fname[l-1] == '/')
263 dr->fname[l-1] = '\0';
264 dname = strrchr(dr->fname, '/');
265 if (dname && dname[1])
269 call("doc:set-name", ci->home, 0, NULL, dname);
271 strcat(dr->fname, "/");
278 DEF_CMD(dir_revisited)
280 struct directory *dr = ci->home->doc_data;
284 /* Being buried, not visited */
287 if (stat(dr->fname, &st) == 0 &&
288 (st.st_ino != dr->stat.st_ino ||
289 st.st_dev != dr->stat.st_dev ||
290 st.st_mtime != dr->stat.st_mtime ||
291 st.st_mtim.tv_nsec != dr->stat.st_mtim.tv_nsec)) {
293 call("doc:load-file", ci->home, 2, NULL, NULL, -1);
294 asprintf(&msg, "Directory %s reloaded", dr->fname);
295 call("Message", ci->focus, 0, NULL, msg);
301 DEF_CMD(dir_same_file)
305 struct directory *dr = ci->home->doc_data;
309 if (fstat(fd, &stb) != 0)
311 if (! (dr->stat.st_ino == stb.st_ino &&
312 dr->stat.st_dev == stb.st_dev))
314 /* Let's reload it now */
315 home_call(ci->home, "doc:load-file", ci->focus, 0, NULL, NULL, fd);
319 static inline wint_t dir_next(struct pane *p safe, struct doc_ref *r safe, bool bytes)
321 struct directory *dr = p->doc_data;
322 struct dir_ent *d = r->d;
327 if (d == list_last_entry(&dr->ents,
328 struct dir_ent, lst))
331 d = list_next_entry(d, lst);
337 static inline wint_t dir_prev(struct pane *p safe, struct doc_ref *r safe, bool bytes)
339 struct directory *dr = p->doc_data;
340 struct dir_ent *d = r->d;
342 if (d == list_first_entry(&dr->ents, struct dir_ent, lst))
345 d = list_last_entry(&dr->ents, struct dir_ent, lst);
347 d = list_prev_entry(d, lst);
357 return do_char_byte(ci);
362 struct directory *dr = ci->home->doc_data;
363 struct mark *m = ci->mark;
368 mark_to_end(ci->home, m, ci->num != 1);
369 if (list_empty(&dr->ents) || ci->num != 1)
372 m->ref.d = list_first_entry(&dr->ents, struct dir_ent, lst);
377 static void get_stat(struct directory *dr safe, struct dir_ent *de safe)
383 dfd = open(dr->fname, O_RDONLY);
386 if (fstatat(dfd, de->name, &de->st, AT_SYMLINK_NOFOLLOW) != 0) {
387 de->st.st_mode = 0xffff;
389 } else if ((de->st.st_mode & S_IFMT) == S_IFLNK &&
390 fstatat(dfd, de->name, &st, 0) == 0 &&
391 (st.st_mode & S_IFMT) == S_IFDIR)
396 static char *fmt_num(struct dir_ent *de safe, long num)
398 sprintf(de->nbuf, "%ld", num);
402 static char *save_str(struct dir_ent *de safe, char *str safe)
404 strncpy(de->nbuf, str, sizeof(de->nbuf));
405 de->nbuf[sizeof(de->nbuf)-1] = 0;
409 static char *fmt_date(struct dir_ent *de safe, time_t t, struct pane *p safe)
412 time_t now = time(NULL);
414 if (edlib_testing(p)) {
418 localtime_r(&t, &tm);
419 if (t > now || t < now - 10*30*24*3600)
420 strftime(de->nbuf, sizeof(de->nbuf),
423 strftime(de->nbuf, sizeof(de->nbuf),
428 static char *fmt_size(struct dir_ent *de safe, loff_t size)
431 snprintf(de->nbuf, sizeof(de->nbuf),
433 else if (size < 1024*10)
434 snprintf(de->nbuf, sizeof(de->nbuf),
435 "%ld.%02ldK", size/1024, (size%1023)*100 / 1024);
436 else if (size < 1024*1024)
437 snprintf(de->nbuf, sizeof(de->nbuf),
439 else if (size < 1024L*1024*10)
440 snprintf(de->nbuf, sizeof(de->nbuf),
441 "%ld.%02ldM", size/1024/1024, ((size/1024)%1023)*100 / 1024);
442 else if (size < 1024L*1024*1024)
443 snprintf(de->nbuf, sizeof(de->nbuf),
444 "%ldM", size/1024/1024);
445 else if (size < 1024L*1024*1024*10)
446 snprintf(de->nbuf, sizeof(de->nbuf),
447 "%ld.%02ldG", size/1024/1024/1024, ((size/1024/1024)%1023)*100 / 1024);
449 snprintf(de->nbuf, sizeof(de->nbuf),
450 "%ldG", size/1024/1024/1024);
454 static char *pwname(int uid)
456 static int last_uid = -1;
457 static char *last_name = NULL;
460 if (uid != last_uid) {
463 if (pw && pw->pw_name)
464 last_name = strdup(pw->pw_name);
472 static char *grname(int gid)
474 static int last_gid = -1;
475 static char *last_name = NULL;
478 if (gid != last_gid) {
481 if (gr && gr->gr_name)
482 last_name = strdup(gr->gr_name);
490 static const char *_dir_get_attr(struct pane *home safe, struct mark *m safe,
491 const char *attr safe)
494 struct directory *dr = home->doc_data;
500 if (strcmp(attr, "name") == 0) {
502 } else if (strcmp(attr, "type") == 0) {
503 de->nbuf[0] = de->ch;
506 } else if (strcmp(attr, "size") == 0) {
508 return fmt_num(de, de->st.st_size);
509 } else if (strcmp(attr, "hsize") == 0) {
511 if (strchr(".:d", de->ch) &&
513 /* Size might not be reliable for testing */
515 return fmt_size(de, de->st.st_size);
516 } else if (strcmp(attr, "mtime") == 0) {
518 return fmt_num(de, de->st.st_mtime);
519 } else if (strcmp(attr, "mdate") == 0) {
521 return fmt_date(de, de->st.st_mtime, home);
522 } else if (strcmp(attr, "atime") == 0) {
524 return fmt_num(de, de->st.st_atime);
525 } else if (strcmp(attr, "adate") == 0) {
527 return fmt_date(de, de->st.st_atime, home);
528 } else if (strcmp(attr, "ctime") == 0) {
530 return fmt_num(de, de->st.st_ctime);
531 } else if (strcmp(attr, "cdate") == 0) {
533 return fmt_date(de, de->st.st_ctime, home);
534 } else if (strcmp(attr, "uid") == 0) {
536 return fmt_num(de, de->st.st_uid);
537 } else if (strcmp(attr, "gid") == 0) {
539 return fmt_num(de, de->st.st_gid);
540 } else if (strcmp(attr, "user") == 0) {
543 if (edlib_testing(home))
545 n = pwname(de->st.st_uid);
547 return save_str(de, n);
549 return fmt_num(de, de->st.st_uid);
550 } else if (strcmp(attr, "group") == 0) {
553 if (edlib_testing(home))
555 n = grname(de->st.st_gid);
557 return save_str(de, n);
559 return fmt_num(de, de->st.st_gid);
560 } else if (strcmp(attr, "mode") == 0) {
562 return fmt_num(de, de->st.st_mode & 0777);
563 } else if (strcmp(attr, "perms") == 0) {
569 mode = de->st.st_mode;
570 switch (mode & S_IFMT) {
571 case S_IFREG: *c ++ = '-'; break;
572 case S_IFDIR: *c ++ = 'd'; break;
573 case S_IFBLK: *c ++ = 'b'; break;
574 case S_IFCHR: *c ++ = 'c'; break;
575 case S_IFSOCK:*c ++ = 's'; break;
576 case S_IFLNK: *c ++ = 'l'; break;
577 default: *c ++ = '?'; break;
579 if (edlib_testing(home) && de->ch == ':')
580 /* ".." might not be under control of the test */
582 for (i = 0; i < 3; i++) {
583 *c ++ = (mode & 0400) ? 'r':'-';
584 *c ++ = (mode & 0200) ? 'w':'-';
585 *c ++ = (mode & 0100) ? 'x':'-';
590 } else if (strcmp(attr, "suffix") == 0) {
593 if (strchr(".:dL", de->ch))
596 } else if (strcmp(attr, "arrow") == 0) {
597 if (strchr("lL", de->ch))
601 } else if (strcmp(attr, "target") == 0) {
606 if (strchr("lL", de->ch) == NULL)
608 dfd = open(dr->fname, O_RDONLY);
611 len = readlinkat(dfd, de->name, buf, sizeof(buf));
613 if (len <= 0 || len >= (int)sizeof(buf))
616 return strsave(home, buf);
618 return attr_find(de->attrs, attr);
621 DEF_CMD(dir_doc_get_attr)
623 struct mark *m = ci->mark;
624 const char *attr = ci->str;
629 val = _dir_get_attr(ci->home, m, attr);
633 comm_call(ci->comm2, "callback:get_attr", ci->focus, 0, m, val,
638 DEF_CMD(dir_doc_set_attr)
640 struct mark *m = ci->mark;
641 const char *attr = ci->str;
642 const char *val = ci->str2;
648 attr_set_str(&m->ref.d->attrs, attr, val);
649 pane_notify("doc:replaced-attr", ci->home, 1, ci->mark);
653 DEF_CMD(dir_get_attr)
655 struct directory *dr = ci->home->doc_data;
656 const char *attr = ci->str;
662 if ((val = attr_find(ci->home->attrs, attr)) != NULL)
664 else if (strcmp(attr, "heading") == 0)
666 else if (strcmp(attr, "render-default") == 0)
668 else if (strcmp(attr, "render-simple") == 0)
670 else if (strcmp(attr, "view-default") == 0)
672 else if (strcmp(attr, "doc-type") == 0)
674 else if (strcmp(attr, "line-format") == 0)
676 else if (strcmp(attr, "filename") == 0)
680 comm_call(ci->comm2, "callback:get_attr", ci->focus, 0, NULL, val);
684 DEF_CMD(dir_val_marks)
686 struct directory *dr = ci->home->doc_data;
690 if (!ci->mark || !ci->mark2)
693 if (ci->mark->ref.d == ci->mark2->ref.d) {
694 if (ci->mark->ref.ignore < ci->mark2->ref.ignore)
696 LOG("dir_val_marks: same buf, bad offset: %u, %u",
697 ci->mark->ref.ignore, ci->mark2->ref.ignore);
700 if (ci->mark->ref.d == NULL) {
701 LOG("dir_val_marks: mark.d is NULL");
705 list_for_each_entry(de, &dr->ents, lst) {
706 if (ci->mark->ref.d == de)
708 if (ci->mark2->ref.d == de) {
711 LOG("dir_val_marks: mark2.d found before mark1");
715 if (ci->mark2->ref.d == NULL) {
718 LOG("dir_val_marks: mark2.d (NULL) found before mark1");
722 LOG("dir_val_marks: Neither mark found in de list");
724 LOG("dir_val_marks: mark2 not found in de list");
730 struct directory *dr = ci->home->doc_data;
732 while (!list_empty(&dr->ents)) {
733 struct dir_ent *de = list_entry(dr->ents.next,
734 struct dir_ent, lst);
736 attr_free(&de->attrs);
744 DEF_CMD(dir_shares_ref)
749 DEF_CMD(dir_debug_mark)
752 struct mark *m = ci->mark;
755 if (!m || m->owner != ci->home || !ci->comm2)
759 ret = strdup("M:FREED");
761 ret = strdup("M:EOF");
763 asprintf(&ret, "M:%s(#%x)", de->name, m->ref.ignore);
764 comm_call(ci->comm2, "cb", ci->focus, 0, NULL, ret);
769 static struct map *dirview_map;
770 DEF_LOOKUP_CMD(dirview_handle, dirview_map);
772 static int dir_open(struct pane *focus safe,
773 struct mark *m, bool other, bool follow)
775 /* close this pane, open the given file. */
777 struct pane *par, *p;
779 char *dirname, *basename, *type;
783 m = call_ret(mark, "doc:point", focus);
786 dirname = pane_attr_get(focus, "filename");
787 basename = pane_mark_attr(focus, m, "name");
788 type = pane_mark_attr(focus, m, "type");
789 if (!dirname || !basename || !type)
792 asprintf(&fname, "%s/%s", dirname, basename);
796 if (follow && (type[0] == 'l' || type[0] == 'L')) {
797 /* Fname is a symlink. Read it and open
798 * that directly. Only follow this step once.
803 ret = readlink(fname, path, sizeof(path));
804 if (ret > 0 && ret < (int)sizeof(path)) {
807 asprintf(&fname, "%s", path);
809 asprintf(&fname, "%s/%s", dirname, path);
814 fd = open(fname, O_RDONLY);
816 p = call_ret(pane, "doc:open", focus, fd, NULL, fname);
819 p = call_ret(pane, "doc:from-text", focus, 0, NULL, fname,
820 0, NULL, "File not found\n");
825 par = home_call_ret(pane, focus, "DocPane", p);
827 pane_take_focus(par);
830 par = call_ret(pane, "OtherPane", focus);
832 par = call_ret(pane, "ThisPane", focus);
834 p = home_call_ret(pane, p, "doc:attach-view", par);
840 static int dir_open_alt(struct pane *focus safe,
841 struct mark *m, char cmd)
843 /* close this pane, open the given file. */
846 char *dirname, *basename;
852 dirname = pane_attr_get(focus, "filename");
853 basename = pane_mark_attr(focus, m, "name");
854 if (!dirname || !basename)
857 asprintf(&fname, "%s/%s", dirname, basename);
860 fd = open(fname, O_RDONLY);
863 struct pane *new = call_ret(pane, "doc:open", focus,
868 snprintf(buf, sizeof(buf), "cmd-%c", cmd);
869 p = call_ret(pane, "ThisPane", focus);
873 p = home_call_ret(pane, new, "doc:attach-view", p,
876 struct pane *doc = call_ret(pane, "doc:from-text", focus,
878 0, NULL, "File not found\n");
881 p = call_ret(pane, "ThisPane", focus);
884 p = home_call_ret(pane, doc, "doc:attach-view", p, 1);
893 return dir_open(ci->focus, ci->mark, False, ci->num == 1);
896 DEF_CMD(dir_do_open_other)
898 return dir_open(ci->focus, ci->mark, True, ci->num == 1);
901 DEF_CMD(dir_do_reload)
903 return call("doc:load-file", ci->focus, 0, NULL, NULL, -1);
906 DEF_CMD(dir_do_mark_del)
908 call("doc:set-attr", ci->focus, 0, ci->mark, "dir-cmd",
910 call("doc:EOL", ci->focus, 1, ci->mark, NULL, 1);
916 call("doc:set-attr", ci->focus, 0, ci->mark, "dir-cmd",
918 call("doc:EOL", ci->focus, 1, ci->mark, NULL, 1);
924 call("doc:set-attr", ci->focus, 0, ci->mark, "dir-cmd",
926 call("doc:EOL", ci->focus, 1, ci->mark, NULL, 1);
930 DEF_CMD(dir_un_mark_back)
932 call("doc:EOL", ci->focus, -2, ci->mark);
933 call("doc:set-attr", ci->focus, 0, ci->mark, "dir-cmd",
940 return call("doc:destroy", ci->home);
943 DEF_CMD(dir_do_special)
945 const char *c = ksuffix(ci, "doc:cmd-");
947 return dir_open_alt(ci->focus, ci->mark, c[0]);
950 static void add_name(struct buf *b safe, char *name safe)
952 if (strchr(name, '\'') == NULL) {
961 if (strchr("\"$`\\", *name))
963 buf_append_byte(b, *name);
970 static char *collect_names(struct pane *p safe, char *type,
973 struct mark *m = mark_new(p);
979 while (type && doc_following(p, m) != WEOF) {
981 t = pane_mark_attr(p, m, "dir-cmd");
982 if (!t || strcmp(t, type) != 0) {
986 name = pane_mark_attr(p, m, "name");
987 call("doc:set-attr", p, 0, m, "dir-cmd");
994 if (!b.len && mark) {
995 char *name = pane_mark_attr(p, mark, "name");
999 return buf_final(&b);
1002 DEF_CMD(dir_expunge)
1007 names = collect_names(ci->focus, "D", NULL);
1009 if (!names || !names[0]) {
1011 call("Message:modal", ci->focus, 0, NULL,
1012 "No files marked for deletion");
1015 cmd = strconcat(ci->focus, "rm -f ", names);
1016 p = call_ret(pane, "attach-shell-prompt", ci->focus, 0, NULL, cmd);
1018 // put cursor after the "-f"
1019 call("doc:file", p, -1);
1020 call("doc:char", p, 5);
1021 pane_add_notify(ci->home, p, "Notify:Close");
1027 DEF_CMD(dir_chmodown)
1033 if (strcmp(ci->key, "K:A-o") == 0)
1038 names = collect_names(ci->focus, "*", ci->mark);
1039 if (!names || !*names) {
1041 call("Message:modal", ci->focus, 0, NULL,
1042 strconcat(ci->focus, "No file for ", which));
1045 cmd = strconcat(ci->focus, which, " ", names);
1046 p = call_ret(pane, "attach-shell-prompt", ci->focus, 0, NULL, cmd);
1048 call("doc:file", p, -1);
1049 call("doc:char", p, strlen(which) + 1);
1050 pane_add_notify(ci->home, p, "Notify:Close");
1060 char *dirname = pane_attr_get(ci->focus, "dirname");
1066 names = collect_names(ci->focus, "*", NULL);
1067 if (names && *names) {
1068 cmd = strconcat(ci->focus, "mv --target-directory ",
1070 prefix = strlen(cmd) - strlen(names);
1073 names = collect_names(ci->focus, NULL, ci->mark);
1074 if (!names || !*names) {
1076 call("Message:modal", ci->focus, 0, NULL,
1077 "No file for rename");
1080 cmd = strconcat(ci->focus, "mv", names, " ", dirname);
1081 prefix = strlen(cmd);
1083 p = call_ret(pane, "attach-shell-prompt", ci->focus, 0, NULL, cmd);
1085 call("doc:file", p, -1);
1086 call("doc:char", p, prefix);
1087 pane_add_notify(ci->home, p, "Notify:Close");
1093 DEF_CMD(dirview_attach)
1097 p = call_ret(pane, "attach-viewer", ci->focus);
1100 p = pane_register(p, 0, &dirview_handle.c);
1103 attr_set_str(&p->attrs, "line-format",
1104 "<fg:green-40>%flag</> <fg:red>%perms</> %mdate:13 %user:10 %group:10%hsize:-6 <fg:blue>%name%suffix</>%arrow<fg:green-30>%target</>");
1105 attr_set_str(&p->attrs, "heading",
1106 "<bold,fg:blue,underline> Perms Mtime Owner Group Size File Name</>");
1108 comm_call(ci->comm2, "cb", p);
1112 DEF_CMD(dirview_clone)
1116 p = pane_register(ci->focus, 0, &dirview_handle.c);
1120 pane_clone_children(ci->home, p);
1124 DEF_CMD(dirview_doc_get_attr)
1126 struct mark *m = ci->mark;
1127 const char *attr = ci->str;
1132 if (strcmp(attr, "flag") != 0)
1133 return Efallthrough;
1134 val = pane_mark_attr(ci->home->parent, m, "dir-cmd");
1137 comm_call(ci->comm2, "cb", ci->focus, 0, m, val, 0, NULL, attr);
1141 DEF_CMD(dirview_close_notify)
1143 /* shell window closed, maybe something was changed - check */
1144 if (ci->key[0] == 'c') {
1146 call("doc:notify:doc:revisit", ci->focus, 1);
1149 call_comm("event:timer", ci->home, &dirview_close_notify, 500);
1153 void edlib_init(struct pane *ed safe)
1155 call_comm("global-set-command", ed, &dir_new, 0, NULL, "attach-doc-dir");
1156 call_comm("global-set-command", ed, &dir_new2, 0, NULL, "open-doc-dir");
1158 dir_map = key_alloc();
1159 key_add_chain(dir_map, doc_default_cmd);
1161 key_add(dir_map, "doc:load-file", &dir_load_file);
1162 key_add(dir_map, "doc:same-file", &dir_same_file);
1163 key_add(dir_map, "doc:set-ref", &dir_set_ref);
1164 key_add(dir_map, "doc:get-attr", &dir_doc_get_attr);
1165 key_add(dir_map, "doc:set-attr", &dir_doc_set_attr);
1166 key_add(dir_map, "doc:char", &dir_char);
1167 key_add(dir_map, "doc:notify:doc:revisit", &dir_revisited);
1168 key_add(dir_map, "doc:debug:mark", &dir_debug_mark);
1170 key_add(dir_map, "doc:shares-ref", &dir_shares_ref);
1172 key_add(dir_map, "get-attr", &dir_get_attr);
1173 key_add(dir_map, "Close", &dir_destroy);
1174 if(0)key_add(dir_map, "debug:validate-marks", &dir_val_marks);
1176 call_comm("global-set-command", ed, &dirview_attach, 0, NULL,
1179 dirview_map = key_alloc();
1180 key_add(dirview_map, "doc:cmd-f", &dir_do_open);
1181 key_add(dirview_map, "doc:cmd-o", &dir_do_open_other);
1182 key_add(dirview_map, "doc:cmd-\n", &dir_do_open);
1183 key_add(dirview_map, "doc:cmd:Enter", &dir_do_open);
1184 key_add(dirview_map, "doc:cmd-g", &dir_do_reload);
1185 key_add(dirview_map, "doc:cmd-q", &dir_do_quit);
1186 key_add(dirview_map, "doc:cmd-d", &dir_do_mark_del);
1187 key_add(dirview_map, "doc:cmd-m", &dir_do_mark);
1188 key_add(dirview_map, "doc:cmd-u", &dir_un_mark);
1189 key_add(dirview_map, "doc:cmd-x", &dir_expunge);
1190 key_add(dirview_map, "doc:cmd-r", &dir_rename);
1191 key_add(dirview_map, "K:A-o", &dir_chmodown);
1192 key_add(dirview_map, "K:A-m", &dir_chmodown);
1193 key_add(dirview_map, "K:Del", &dir_un_mark_back);
1194 key_add_range(dirview_map, "doc:cmd-A", "doc:cmd-Z", &dir_do_special);
1195 key_add(dirview_map, "doc:get-attr", &dirview_doc_get_attr);
1196 key_add(dirview_map, "Clone", &dirview_clone);
1197 key_add(dirview_map, "Notify:Close", &dirview_close_notify);